library(tidyverse)
library(janitor)
library(lubridate)
library(GGally)
library(ggfortify)
library(modelr)
library(tidytext)
library(wordcloud)
library(leaflet)
library(ggridges)
library(ggthemes)
library(relaimpo)
library(tm)
library(SnowballC)
library(wordcloud)
library(RColorBrewer)

Introduction

About the Data

The data is about New York City Airbnb listings in 2019.

The data includes information on, prices, New York neighbourhoods and room types, to name a few. Geospatial coordinates are also present, offering valuable insight into Airbnb locations.

Project Objectives

Primary aim: - Analyse the determinants of airbnb prices - Offer recommendations for customers booking airbnbs - Provide insights for hosts - adopt an appropriate pricing strategy

Overview

  • Ethical Considerations
  • Data Cleaning and Wrangling
  • Exploratory Analysis
  • Text mining
  • Geo-spatial Analysis
  • Model Building
    • Univariate Regression
    • Multivariate Regression
  • Analysis Conclusions
prices <- read_csv("raw_data/AB_NYC_2019.csv") %>% clean_names()

Ethical Considerations

The data contains information on host names and unique IDs. To avoid any ethical issues I chose to remove these variables.

Clean Data

Check Missing Values

Many missing values in last_review and reviews_per_month (10052 rows)

  • last_review will be dropped as inconsequential variable
  • values dropped from reviews_per_month
    • coalescing with mean may warp data too much

Data Cleaning

Exploratory Analysis

Total Number of Bookings per Borough

Manhattan and Brooklyn both by far the most popular areas for Airbnb listings.

Two central Boroughs which may indicate the main reason people book is for holidays / Tourism.

Average Price per Neighbourhood Group


# Average Price per Room
price_per_room <- prices_df %>% 
  # group by room_type
  group_by(room_type) %>% 
  # return average price per room_type
  summarise(avg_price = mean(price)) %>% 
  # create bar chart to visualise result
  ggplot(aes(room_type, avg_price, fill = room_type)) +
  # specify bar chart
  geom_col(show.legend = FALSE) +
  # annotate each bar
  geom_label(mapping = aes(label = round(avg_price, 2)), size = 6, 
             fill = "#F5FFFA", fontface = "bold", 
             # position change to make sure label stays on page
             position = position_stack(vjust = 0.9)) +
  # add theme
  theme_classic() +
  # titles
  labs(x = "Room Type", y = "Average Price ($)", 
       title = "Average Price by Room Type")


# Average price per neighbourhood group
price_room_borough <- prices_df %>% 
  # group by both neighbourhood and room_type
  group_by(neighbourhood_group, room_type) %>% 
  # return average price for each room type in each neighbourhood
  summarise(avg_price = mean(price)) %>% 
  # sort from highest to lowest price
  arrange(desc(avg_price)) %>% 
  # create bar chart
  ggplot(aes(reorder(neighbourhood_group, avg_price), avg_price, 
             fill = room_type)) +
  # specify bar chart
  geom_col(show.legend = FALSE) + 
  # annotate bars
  geom_label(mapping = aes(label = round(avg_price, 2)), size = 2.5, 
             fill = "#F5FFFA", fontface = "bold", hjust = 0.5, 
             # position change to make sure label stays on page
             position = position_stack(vjust = 0.9)) +
  # theme
  theme_classic() +
  # titles
  labs(x = "New York Boroughs", y = "Average Price ($)", 
       title = "Average Price per Borough by Room Type") + 
  # split into room_types
  facet_wrap(~room_type) +
  # flip x and y axis
  coord_flip()
`summarise()` has grouped output by 'neighbourhood_group'. You can override using the `.groups` argument.
# plot both visualisations together
cowplot::plot_grid(price_per_room, price_room_borough, nrow = 2)

The 10 most expensive and cheapest New York Districts

# Top 10 most expensive districts on average
a <- prices_df %>% 
  # group by individual districts within neighbourhoods
  group_by(neighbourhood) %>% 
  # return average price
  summarise(avg_price = mean(price)) %>% 
  # sort from highest to lowest
  arrange(desc(avg_price)) %>% 
  # return the top 10 (out of 218)
  slice(1:10) %>% 
  # create bar plot
  ggplot(aes(reorder(neighbourhood, avg_price), avg_price, 
             fill = neighbourhood)) + 
  # specify bar chart
  geom_col(show.legend = FALSE) +
  # annotate bars 
  geom_label(mapping = aes(label = round(avg_price, 2)), size = 3, 
             fill = "#F5FFFA", fontface = "bold") +
  # theme
  theme_classic() +
  # change x-axis label position
  theme(axis.text.x = element_text(angle = 30, vjust = 0.95, hjust = 1)) +
  # titles
  labs(x = "Neighbourhood", y = "Average Price ($)", 
       title = "The 10 Most Expensive Districts on Average")

# top 10 least expensive districts on average
b <- prices_df %>% 
  # group by individual districts within neighbourhoods
  group_by(neighbourhood) %>% 
  # return average prices
  summarise(avg_price = mean(price)) %>% 
  # arrange from lowest to highest
  arrange(avg_price) %>% 
  # return bottom 10 prices
  slice(1:10) %>% 
  # create bar chart
  ggplot(aes(reorder(neighbourhood, avg_price), avg_price, 
             fill = neighbourhood)) + 
  # specify bar chart
  geom_col(show.legend = FALSE) +
  # annotate bars
  geom_label(mapping = aes(label = round(avg_price, 2)), size = 3, 
             fill = "#F5FFFA", fontface = "bold") +
  # theme
  theme_classic() +
  # change x-axis label position
  theme(axis.text.x = element_text(angle = 30, vjust = 0.95, hjust = 1)) +
  # titles
  labs(x = "Neighbourhood", y = "Average Price ($)", 
       title = "The 10 Least Expensive Districts on Average")

# add plots into the same output
cowplot::plot_grid(a, b, nrow=2)

The 10 most expensive districts are all located in Manhattan apart from, Neponsit (Queens) and WillowBrook (Staten Island)

The majority of the 10 least expensive districts reside in the Bronx, State Island and Queens

Average Reviews by Last Month Review Submitted

Suggests that most people are leaving reviews in the summer, indicating some seasonality to Airbnb booking in New York.

Price Density by Area

# price density by New York Borough
ggplot(
  # create plot for price less than $500
  subset(prices_df, price < 500),aes(x = price)) +
  # specify density plot
  geom_density(
    mapping = aes(fill = neighbourhood_group), 
    bandwidth = 100, alpha = 1, size = 0.5, show.legend = FALSE) +
  # theme
  theme_bw() +
  # show individual density plots for boroughs
  facet_wrap(~neighbourhood_group) +
  # titles
  labs(x = "Price", y = "Density", title = "Price Density by Borough")
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.Warning: Ignoring unknown parameters: `bandwidth`

Pricing density plot reveals that boroughs with fewer amount of bookings (Queens, Staten Island and Bronx) have a higher density of lower prices

Most common areas (Mahattan and Brooklyn) have a wider density plot indicating that prices vary more.

Text Mining

I want to find the words that are associated with different price ranges.

So I need to create new variables which classify the price range of each airbnb

we will define price ranges based around the average price for total bookings

mean price is $142 therefore, low will be less than $100, medium will be between $100 - $200, high will be $200 - $300 and very high will be greater than $300

High/Very High Price Range

# words associated with high prices
high_price_words <- prices_new %>% 
  # filter for High and Very High price classes 
  filter(price_class %in% c("High", "Very High")) %>% 
  # take individual words from the name column
  unnest_tokens(word, name) %>% 
  # take out stop_words 
  anti_join(stop_words) %>% 
  # select word column
  dplyr::select(word)
Joining, by = "word"
# most common words
sorted_hp_words <- high_price_words %>%
  # sort most common words
  count(word, sort = TRUE)

# create word cloud for high/very high price Airbnbs
wordcloud(
  # words to use for word cloud 
  words = sorted_hp_words$word,
  # number of times these words appear
  freq = sorted_hp_words$n, 
  # maximum number of words used
  max.words=60, 
  # no random order
  random.order=FALSE, 
  # proportion of words with a 90 degree angle
  rot.per=0.35, 
  # add colour palette
  colors=brewer.pal(8, "Spectral"))

Words that stand out from wordcloud include: bedroom, apartment, village, luxury, location, Manhattan, spacious, park - start to understand what kind of Airbnbs are being advertised for high prices

Low Price Range

# words associated with low prices
low_price_words <- prices_new %>% 
  # filter for rows associated with low prices
  filter(price_class %in% "Low") %>% 
  # take words from name column
  unnest_tokens(word, name) %>% 
  # remove stop words
  anti_join(stop_words) %>% 
  # select word column
  dplyr::select(word)
Joining, by = "word"
# most common words
sorted_lp_words <- low_price_words %>% 
  # sort most common words
  count(word, sort = TRUE)

wordcloud(
  # select words 
  words = sorted_lp_words$word, 
  # number of times these words appear
  freq = sorted_lp_words$n, 
  # maximum number of words in cloud
  max.words=60, 
  # no random order
  random.order=FALSE, 
  # proportion of words rotated
  rot.per=0.35, 
  # colour palette
  colors=brewer.pal(8, "Spectral"))

when we look at the word cloud of the low price range, we see some similarities with the high price range indicating owners are trying to sell the property as up market.

Big emphasis on property being “private” which is likely to be a big concern for people when not paying that much. In contrast, privacy is a given when paying for high end accommodation.

Medium Price Range

# words associated with medium price range (around the average)
medium_price_words <- prices_new %>% 
  # filter rows associated with medium price range
  filter(price_class %in% "Medium") %>% 
  # select words from name column
  unnest_tokens(word, name) %>% 
  # remove stop words
  anti_join(stop_words) %>% 
  # select words
  dplyr::select(word)
Joining, by = "word"
# most common words
sorted_mp_words <- medium_price_words %>% 
  # sort words
  count(word, sort = TRUE)


# word cloud for medium price range
wordcloud(
  # words to use
  words = sorted_mp_words$word, 
  # frequency words appear
  freq = sorted_mp_words$n, 
  # maximum number of words
  max.words=60, 
  # random order = FALSE --> makes it neat
  random.order=FALSE, 
  # proportion of words rotated
  rot.per=0.35, 
  # colour palette
  colors=brewer.pal(8, "Spectral"))

Not a tremendous amount of difference here, probably as expected it takes a balance between low and high price ranges highlighting privacy as important but also more emphasis on location.

Geo-spatial Analysis

Can we see if the geo-spatial backs up the word cloud and bigram analysis of emphasis on location for medium and low price ranges

Density of New York Boroughs

Density of Price Class

can see the spread of prices based on the area, can see that lower prices tend to located to the perimeter of New York, indicating that location towards centre of Manhattan is a large determinant of price.

Leaflet Map

Leaflet map shows the contrast between extremities and city centre, evidently much higher prices in the city.

Summary

Prices are being impacted by: - location - type of accommodation

Model Building - Linear Regression

Approach: - manual - good way to become familiar with data - avoid over or under fitting the model

Goals of the model:

Distribution of Price

Dependent variable: Price

Important to investigate its distribution as it may require a transformation to create a better fit for the model

Price is heavily skewed to the right indicates a log transformation is needed to get a normal bell shaped distribution

# mean price 
mean_price <- prices_new %>% 
  summarise(m_price = mean(price))


# log bell shaped distribution 
prices_new %>% 
  # set price on x-axis as a natural logarithm
  ggplot(aes(log(price))) +
  # specify histogram
  geom_histogram(bins = 40, aes(y = ..density..), fill = "red") + 
  # insert density area
  geom_density(alpha = 0.2, fill = "red") +
  # insert line to indicate average
  geom_vline(data = mean_price, aes(xintercept =  log(m_price)), size = 1, 
             linetype = 2) +
  # theme
  theme_bw() +
  # title
  labs(title = "Distribution of log(price)")

Finalising Data for Model

Dropped variables:

  • ‘name’ - use dummy variables for important words and bigrams instead
  • ‘neighbourhood’ - use the categorical variable for five New York Boroughs
  • ‘price_class’ - high correlated with dependent variable price

Dummy variables created from text analysis:

  • ‘apartment’
  • ‘private’
  • ‘central park’
prices_reg_df <- prices_new %>% 
  # create dummy variables for chosen words that may impact price
  mutate(apartment_ad = if_else(str_detect(name, "[Aa]partment"), "YES", "NO"),
         private_ad = if_else(str_detect(name, "[Pp]rivate"), "YES", "NO"),
         central_park_ad = if_else(str_detect(name, "[Cc]entral [Pp]ark"), 
                                   "YES", "NO"),
         ) %>% 
  # remove variables not considered for model
  dplyr::select(-c(name, neighbourhood, price_class)) %>% 
  # log transform price
  mutate(log_price = log(price + 1)) %>% 
  # bring price to the front
  dplyr::select(log_price,price, everything()) %>% 
  # change all character variables to factor
  mutate(across(.cols = is.character, 
                .fns = as_factor)) %>% 
  # remove original price variable
  dplyr::select(-price)
  

Check alias() function to check for multicollinearity

# check for multicollinearity
alias(lm(log_price ~ ., data = prices_reg_df))
Model :
log_price ~ neighbourhood_group + latitude + longitude + room_type + 
    minimum_nights + number_of_reviews + reviews_per_month + 
    calculated_host_listings_count + availability_365 + last_review_month + 
    name_length + apartment_ad + private_ad + central_park_ad

Data is now ready to be used in regression analysis

Use ggpairs() to investigate which variables are highly correlated with price.

Data set is large so relationship analysis will be divided into numeric and non-numeric datatypes

# select variables that are numeric
variable_numeric <- prices_reg_df %>%
  select_if(is.numeric)

# ggpairs with only numeric variables
ggpairs(variable_numeric)

# select variables that are categorical 
variable_nonnumeric <- prices_reg_df %>%
  # use function to return variables that are categorical
  select_if(function(x) !is.numeric(x))

# need to add log_price to non-numeric data 
variable_nonnumeric$log_price <- prices_reg_df$log_price

# non-numeric ggpairs
ggpairs(variable_nonnumeric)

Univariate Regression

Largest correlation with price: longitude - negatively correlated by 0.155 - statistically significant at the 0.001 level of significance.

The non-numeric ggpairs suggests that several variables will be able to explain price variance.

relationship between log(price) and longitude

create model

# linear regression model
model_1 <- lm(log_price ~ longitude, data = prices_reg_df)

Regression Diagnostics

graph 1 (population) - tell us most observations are independence, blue line declines at the end indicating other chunk of observations are not independently distributed. Need another variable.

graph 2 (distribution) - shows a fairly normal distribution, again, a bend at the end indicates model needs more to give a better distribution

graph 3 (homoskedasticity) - There is heteroskedasticity in the model indicated my curve on blue line

graph 4 (outliers) - There is a small number of outliers, and points are not highly leveraged.

model interpretation

# obtain results of regression model
summary(model_1)

Call:
lm(formula = log_price ~ longitude, data = prices_reg_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.7215 -0.4287 -0.0309  0.3813  4.6629 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -335.83166    5.02723  -66.80   <2e-16 ***
longitude     -4.60491    0.06798  -67.74   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6256 on 38835 degrees of freedom
Multiple R-squared:  0.1057,    Adjusted R-squared:  0.1056 
F-statistic:  4589 on 1 and 38835 DF,  p-value: < 2.2e-16

coefficient:

  • An increase in longitude by one unit is associated with a change in price by 99.9% on average.

R-squared - longitude explains 10.6% of the variance of airbnb price.

Multivariate Regression

From the ggpairs plot and previous analysis, neighbourhood_group is suggests having an impact on airbnb prices, I will add this next.

Relationship between Price and Borough

regression diagnostics

graph 1 - independent population (achieved) graph 2 - still a skew in distribution graph 3 - Homoskdasticity (achieved) graph 4 - No highly leveraged points, but there are still (potentially) a few outliers

model interpretation

# regression results
summary(model_2)

Call:
lm(formula = log_price ~ longitude + neighbourhood_group, data = prices_reg_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.5931 -0.4275 -0.0334  0.3609  4.6367 

Coefficients:
                                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)                      -3.283e+02  7.499e+00 -43.780  < 2e-16 ***
longitude                        -4.501e+00  1.014e-01 -44.391  < 2e-16 ***
neighbourhood_groupManhattan      2.790e-01  7.033e-03  39.667  < 2e-16 ***
neighbourhood_groupQueens         1.506e-01  1.294e-02  11.643  < 2e-16 ***
neighbourhood_groupStaten Island -9.425e-01  3.777e-02 -24.956  < 2e-16 ***
neighbourhood_groupBronx         -6.477e-02  2.201e-02  -2.942  0.00326 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6038 on 38831 degrees of freedom
Multiple R-squared:  0.167, Adjusted R-squared:  0.1669 
F-statistic:  1557 on 5 and 38831 DF,  p-value: < 2.2e-16

coefficients:

  • all statistically significant at levels of significance - can reject the null hypothesis and say that coefficients are statistically different from zero.

  • An increase in Manhattan by one unit (zero to one) is associated with a change in price by (e^0.27-1) * 100 = 31%, holding all other factors constant

  • An increase in Staten Island by one unit (zero to one) is associated with a change in price by (e^-0.94 - 1) * 100 = -60.9%, holding all other factors constant

Anova function

# function to check if dummy variables are useful
anova(model_1, model_2)
Analysis of Variance Table

Model 1: log_price ~ longitude
Model 2: log_price ~ longitude + neighbourhood_group
  Res.Df   RSS Df Sum of Sq      F    Pr(>F)    
1  38835 15198                                  
2  38831 14156  4    1042.4 714.85 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

anova function confirms the neighbourhood_group dummy variable was good to include in the model.

Model 3

before adding a new variable, must check the residuals of this model against the remaining variables in the dataset

check residuals

# non-numeric ggpairs
ggpairs(price_resid_nonnumeric)

From numeric variables, availability_365 has the highest correlation with price a positive correlation of 0.131 (statistically significant).

However, the non-numeric variables indicate that room type may present a better explanation of price variance.

Comparison between impact of Room Type and Availability on Price


avail_plot <- price_resid_numeric %>% 
  ggplot(aes(availability_365, resid)) + 
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, colour = "red") + 
  theme_bw() +
  labs(x = "Yearly Room Availability", y = "Residuals", 
       title = "Relationship Residuals and Availability") +
  theme(plot.title = element_text(size=10))

room_type_plot <- price_resid_nonnumeric %>% 
  ggplot(aes(room_type, resid, fill = room_type)) + 
  geom_boxplot(show.legend = FALSE) +
  theme_bw() + 
  labs(title = "Relationship Residuals and Room Type",
       x = "Room Type", y = "Residuals") +
  theme(plot.title = element_text(size=10))

cowplot::plot_grid(avail_plot, room_type_plot, nrow = 1) 
`geom_smooth()` using formula = 'y ~ x'

The comparison suggests room type should offer more explanation.

graph 1 - residuals are independent graph 2 - the residuals seem to be increasingly not distributed around zero with more skew at the beginning and end graph 3 - conditional variance of residuals is constant (homoskedasticity)

# model results
summary(model_3)

Call:
lm(formula = log_price ~ longitude + neighbourhood_group + room_type, 
    data = prices_reg_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.9468 -0.2982 -0.0401  0.2313  4.9777 

Coefficients:
                                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)                      -2.133e+02  5.899e+00 -36.160  < 2e-16 ***
longitude                        -2.942e+00  7.978e-02 -36.873  < 2e-16 ***
neighbourhood_groupManhattan      2.397e-01  5.495e-03  43.611  < 2e-16 ***
neighbourhood_groupQueens         1.183e-01  1.010e-02  11.709  < 2e-16 ***
neighbourhood_groupStaten Island -6.894e-01  2.951e-02 -23.358  < 2e-16 ***
neighbourhood_groupBronx         -5.006e-02  1.718e-02  -2.914  0.00357 ** 
room_typeEntire home/apt          7.443e-01  4.943e-03 150.572  < 2e-16 ***
room_typeShared room             -3.956e-01  1.659e-02 -23.841  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4711 on 38829 degrees of freedom
Multiple R-squared:  0.493, Adjusted R-squared:  0.4929 
F-statistic:  5393 on 7 and 38829 DF,  p-value: < 2.2e-16

As anticipated, room_type greatly enhanced the explanation of the model. The R-squared suggests the model explains 49.3% of the variance in price.

The variable coefficients are all statistically significant therefore can be interpreted.

Model 4

Check residuals

As before, availability is displaying by far the strongest correlation, and there does not seem to be any stand out non-numeric variables. Therefore, room availability will be used in the next model.

graph 1 - model population is independently distributed graph 2 - still skew at both ends of graph, indicating graph is only somewhat normally distributed graph 3 - conditional variance of residuals is constant (homoskedastic)

# model results
summary(model_4)

Call:
lm(formula = log_price ~ longitude + neighbourhood_group + room_type + 
    availability_365, data = prices_reg_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.9243 -0.2919 -0.0406  0.2328  5.0684 

Coefficients:
                                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)                      -2.274e+02  5.815e+00 -39.107  < 2e-16 ***
longitude                        -3.131e+00  7.863e-02 -39.819  < 2e-16 ***
neighbourhood_groupManhattan      2.332e-01  5.408e-03  43.122  < 2e-16 ***
neighbourhood_groupQueens         1.041e-01  9.943e-03  10.466  < 2e-16 ***
neighbourhood_groupStaten Island -7.852e-01  2.915e-02 -26.939  < 2e-16 ***
neighbourhood_groupBronx         -7.984e-02  1.692e-02  -4.719 2.38e-06 ***
room_typeEntire home/apt          7.438e-01  4.861e-03 153.004  < 2e-16 ***
room_typeShared room             -4.276e-01  1.634e-02 -26.166  < 2e-16 ***
availability_365                  6.674e-04  1.840e-05  36.271  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4633 on 38828 degrees of freedom
Multiple R-squared:  0.5096,    Adjusted R-squared:  0.5095 
F-statistic:  5043 on 8 and 38828 DF,  p-value: < 2.2e-16

R-squared has only increased marginally to 0.51 - model explains 51% of the variance in price

All variables in the model are statistically significant

Adding an interaction term

potential terms:

  • longitude:neighbourhood_group
  • longitude:room_type
  • longitude:availability_365
  • neighbourhood_group:room_type
  • neighbourhood_group:availability_365
  • room_type:availability_365

Through process of elimination, longitude:neighbourhood_group

Plotting the interaction

# add model residuals to data
price_resid <- prices_reg_df %>% 
  # add model residuals
  add_residuals(model_5) %>% 
  # remove log_price
  dplyr::select(-log_price)


# check the interaction between longitude and neighbourhood_group
coplot(resid ~ longitude | neighbourhood_group,
       # give an action to be carried out in each panel
       panel = function(x, y, ...){
         # plot coordinates of x and y
         points(x, y)
         # insert linear model line
         abline(lm(y ~ x), col = "blue")
       },
       data = price_resid, rows = 1)

Model Diagnostics

graph 1 - population is independent graph 2 - model still not completely evenly distributed around 0 graph 3 - conditional variance of residuals is constant (homoskedasticity) graph 4 - there are still some outliers, but no highly leveraged points

Model Summary

# model results
summary(model_5)

Call:
lm(formula = log_price ~ longitude + neighbourhood_group + room_type + 
    availability_365 + longitude:neighbourhood_group, data = prices_reg_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.9155 -0.2856 -0.0436  0.2300  5.1577 

Coefficients:
                                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                -2.378e+02  1.031e+01 -23.065  < 2e-16 ***
longitude                                  -3.272e+00  1.394e-01 -23.468  < 2e-16 ***
neighbourhood_groupManhattan               -3.219e+02  1.557e+01 -20.677  < 2e-16 ***
neighbourhood_groupQueens                   1.761e+02  1.349e+01  13.053  < 2e-16 ***
neighbourhood_groupStaten Island            3.119e+02  5.582e+01   5.587 2.33e-08 ***
neighbourhood_groupBronx                    2.388e+02  3.601e+01   6.631 3.38e-11 ***
room_typeEntire home/apt                    7.255e-01  4.820e-03 150.506  < 2e-16 ***
room_typeShared room                       -4.263e-01  1.609e-02 -26.491  < 2e-16 ***
availability_365                            6.323e-04  1.815e-05  34.842  < 2e-16 ***
longitude:neighbourhood_groupManhattan     -4.354e+00  2.105e-01 -20.689  < 2e-16 ***
longitude:neighbourhood_groupQueens         2.382e+00  1.825e-01  13.053  < 2e-16 ***
longitude:neighbourhood_groupStaten Island  4.219e+00  7.533e-01   5.601 2.15e-08 ***
longitude:neighbourhood_groupBronx          3.233e+00  4.874e-01   6.633 3.32e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4562 on 38824 degrees of freedom
Multiple R-squared:  0.5246,    Adjusted R-squared:  0.5244 
F-statistic:  3570 on 12 and 38824 DF,  p-value: < 2.2e-16

Model Summary:

  • All explanatory variables are statistically significant

  • R-squared: model explains 52.5% of variance in price

    • model suffices as an ok explanation
  • Room_type had the greatest influence on the model

    • interpret the coefficient for room_type- entire/apt:
      • An increase in entire/apt by one unit (zero to one) is associated with a change in price by (e^0.73-1) * 100 = 107.5%, holding all other factors constant

Variable relative importance

# function to calculate variable importance
calc.relimp(model_5, type = "lmg", rela = TRUE)
Response variable: log_price 
Total response variance: 0.4375758 
Analysis based on 38837 observations 

12 Regressors: 
Some regressors combined in groups: 
        Group  neighbourhood_group : neighbourhood_groupManhattan neighbourhood_groupQueens neighbourhood_groupStaten Island neighbourhood_groupBronx 
        Group  room_type : room_typeEntire home/apt room_typeShared room 
        Group  longitude:neighbourhood_group : longitude:neighbourhood_groupManhattan longitude:neighbourhood_groupQueens longitude:neighbourhood_groupStaten Island longitude:neighbourhood_groupBronx 

 Relative importance of 5 (groups of) regressors assessed: 
 neighbourhood_group room_type longitude:neighbourhood_group longitude availability_365 
 
Proportion of variance explained by model: 52.46%
Metrics are normalized to sum to 100% (rela=TRUE). 

Relative importance metrics: 

                                     lmg
neighbourhood_group           0.15650767
room_type                     0.66297003
longitude:neighbourhood_group 0.03891208
longitude                     0.11782702
availability_365              0.02378320

Average coefficients for different model sizes: 

                                                  1group       2groups       3groups       4groups       5groups
longitude                                  -4.6049149739 -4.1229356833 -4.051358e+00 -3.897941e+00 -3.271661e+00
neighbourhood_groupManhattan                0.3819453426  0.3211757943 -1.070997e+02 -2.511174e+02 -3.218665e+02
neighbourhood_groupQueens                  -0.2087349654 -0.0655638368  8.477795e+01  1.718999e+02  1.760652e+02
neighbourhood_groupStaten Island           -0.2501789720 -0.4947127336  9.315515e+01  2.287431e+02  3.118768e+02
neighbourhood_groupBronx                   -0.3667919672 -0.2372578195  1.284316e+02  2.514425e+02  2.387853e+02
room_typeEntire home/apt                    0.8196414599  0.7858184226  7.602355e-01  7.344142e-01  7.254613e-01
room_typeShared room                       -0.3741098014 -0.3871140671 -4.059261e-01 -4.118401e-01 -4.263369e-01
availability_365                            0.0003947914  0.0005442186  6.232164e-04  6.210777e-04  6.323326e-04
longitude:neighbourhood_groupManhattan               NaN           NaN -5.805751e+00 -5.096923e+00 -4.354219e+00
longitude:neighbourhood_groupQueens                  NaN           NaN  4.586125e+00  3.487592e+00  2.381855e+00
longitude:neighbourhood_groupStaten Island           NaN           NaN  5.069942e+00  4.647986e+00  4.219434e+00
longitude:neighbourhood_groupBronx                   NaN           NaN  6.959079e+00  5.105896e+00  3.232856e+00

Variables of relative importance:

Room type, perhaps unsurprisingly, is the most relevant variable for explaining variations in price. The least explanatory is availability_365

Conclusion and Recommendations

For Consumers:

For hosts: - How you describe your property doesn’t translate to being able to charge higher prices - will be able to charge a much higher price for property listed as whole apartment - property availability does not have much impact on price

Future analysis

More data on:

-yearly data - time-series analysis - identify cyclical trends

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KEdHYWxseSkKbGlicmFyeShnZ2ZvcnRpZnkpCmxpYnJhcnkobW9kZWxyKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHdvcmRjbG91ZCkKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGdncmlkZ2VzKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHJlbGFpbXBvKQpsaWJyYXJ5KHRtKQpsaWJyYXJ5KFNub3diYWxsQykKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCgojIEludHJvZHVjdGlvbgoKIyMgQWJvdXQgdGhlIERhdGEKClRoZSBkYXRhIGlzIGFib3V0IE5ldyBZb3JrIENpdHkgQWlyYm5iIGxpc3RpbmdzIGluIDIwMTkuIAoKVGhlIGRhdGEgaW5jbHVkZXMgaW5mb3JtYXRpb24gb24sIHByaWNlcywgTmV3IFlvcmsgbmVpZ2hib3VyaG9vZHMgYW5kIHJvb20gdHlwZXMsCnRvIG5hbWUgYSBmZXcuIEdlb3NwYXRpYWwgY29vcmRpbmF0ZXMgYXJlIGFsc28gcHJlc2VudCwgb2ZmZXJpbmcgdmFsdWFibGUgaW5zaWdodAppbnRvIEFpcmJuYiBsb2NhdGlvbnMuIAoKIyMgUHJvamVjdCBPYmplY3RpdmVzCgpQcmltYXJ5IGFpbToKLSBBbmFseXNlIHRoZSBkZXRlcm1pbmFudHMgb2YgYWlyYm5iIHByaWNlcwogIC0gT2ZmZXIgcmVjb21tZW5kYXRpb25zIGZvciBjdXN0b21lcnMgYm9va2luZyBhaXJibmJzCiAgLSBQcm92aWRlIGluc2lnaHRzIGZvciBob3N0cyAtIGFkb3B0IGFuIGFwcHJvcHJpYXRlIHByaWNpbmcgc3RyYXRlZ3kKICAKIyMgT3ZlcnZpZXcKCi0gRXRoaWNhbCBDb25zaWRlcmF0aW9ucwotIERhdGEgQ2xlYW5pbmcgYW5kIFdyYW5nbGluZwotIEV4cGxvcmF0b3J5IEFuYWx5c2lzCi0gVGV4dCBtaW5pbmcKLSBHZW8tc3BhdGlhbCBBbmFseXNpcwotIE1vZGVsIEJ1aWxkaW5nCiAgLSBVbml2YXJpYXRlIFJlZ3Jlc3Npb24KICAtIE11bHRpdmFyaWF0ZSBSZWdyZXNzaW9uCi0gQW5hbHlzaXMgQ29uY2x1c2lvbnMKCgpgYGB7cn0KcHJpY2VzIDwtIHJlYWRfY3N2KCJyYXdfZGF0YS9BQl9OWUNfMjAxOS5jc3YiKSAlPiUgY2xlYW5fbmFtZXMoKQpgYGAKCgojIEV0aGljYWwgQ29uc2lkZXJhdGlvbnMKClRoZSBkYXRhIGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIGhvc3QgbmFtZXMgYW5kIHVuaXF1ZSBJRHMuIFRvIGF2b2lkIGFueSBldGhpY2FsCmlzc3VlcyBJIGNob3NlIHRvIHJlbW92ZSB0aGVzZSB2YXJpYWJsZXMuIAoKLSBSZWR1Y2UgYmlhcwotIEZvbGxvdyBsYXcgCi0gZW5zdXJlIGNvbnN1bWVyIHRydXN0CgojIENsZWFuIERhdGEKCiMjIENoZWNrIE1pc3NpbmcgVmFsdWVzCgpgYGB7cn0KIyBjaGVjayBmb3IgbWlzc2luZyB2YWx1ZXMKcHJpY2VzICU+JSAKICAjIHJldHVybiB0b3RhbCBtaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGNvbHVtbgogIHN1bW1hcmlzZSgKICAgIGFjcm9zcygKICAgICAgLmNvbHMgPSBldmVyeXRoaW5nKCksCiAgICAgIC5mbnMgPSB+c3VtKGlzLm5hKC54KSkKICAgICkKICApICU+JSAKICAjIHNlbGVjdCBjb2x1bW5zIHRoYXQgaGF2ZSBtaXNzaW5nIHZhbHVlcwogIGRwbHlyOjpzZWxlY3QoYyhuYW1lLCBob3N0X25hbWUsIGxhc3RfcmV2aWV3LCByZXZpZXdzX3Blcl9tb250aCkpCmBgYApNYW55IG1pc3NpbmcgdmFsdWVzIGluIGxhc3RfcmV2aWV3IGFuZCByZXZpZXdzX3Blcl9tb250aCAoMTAwNTIgcm93cykKCi0gbGFzdF9yZXZpZXcgd2lsbCBiZSBkcm9wcGVkIGFzIGluY29uc2VxdWVudGlhbCB2YXJpYWJsZQotIHZhbHVlcyBkcm9wcGVkIGZyb20gcmV2aWV3c19wZXJfbW9udGgKICAtIGNvYWxlc2Npbmcgd2l0aCBtZWFuIG1heSB3YXJwIGRhdGEgdG9vIG11Y2ggCgoKIyMgRGF0YSBDbGVhbmluZwoKYGBge3J9CnByaWNlc19kZiA8LSBwcmljZXMgJT4lIAogICMgZHJvcCBob3N0IG5hbWVzIGFuZCBob3N0X2lkIChldGhpY2FsKQogIGRwbHlyOjpzZWxlY3QoLWMoaG9zdF9uYW1lLCBob3N0X2lkLCBpZCkpICU+JSAKICAjIHRha2Ugb3V0IG1vbnRoIGZyb20gbGFzdF9yZXZpZXcgCiAgbXV0YXRlKGxhc3RfcmV2aWV3X21vbnRoID0gbW9udGgobGFzdF9yZXZpZXcsIGxhYmVsID0gVFJVRSksCiAgICAgICAgICMgdGFrZSBuYW1lIGxlbmd0aAogICAgICAgICBuYW1lX2xlbmd0aCA9IHN0cl9sZW5ndGgobmFtZSksCiAgICAgICAgICMgaW1wdXRlIG1pc3NpbmcgdmFsdWVzIHdpdGggYXZlcmFnZQogICAgICAgICByZXZpZXdzX3Blcl9tb250aCA9IGNvYWxlc2NlKHJldmlld3NfcGVyX21vbnRoLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKHJldmlld3NfcGVyX21vbnRoLCBuYS5ybSA9IFRSVUUpKQogICAgICAgICApICU+JSAKICAjIHJlbW92ZSBsYXN0X3JldmlldyBjb2x1bW4KICBkcGx5cjo6c2VsZWN0KC1sYXN0X3JldmlldykKCiMgY2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzCnByaWNlc19kZiAlPiUgCiAgIyByZXR1cm4gdG90YWwgbWlzc2luZyB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4KICBzdW1tYXJpc2UoCiAgICBhY3Jvc3MoCiAgICAgIC5jb2xzID0gZXZlcnl0aGluZygpLAogICAgICAuZm5zID0gfnN1bShpcy5uYSgueCkpCiAgICApCiAgKSAlPiUgCiAgIyBjb2x1bW5zIHRoYXQgcmV0dXJuZWQgbWlzc2luZyB2YWx1ZXMKICBkcGx5cjo6c2VsZWN0KGMobmFtZSwgbGFzdF9yZXZpZXdfbW9udGgsIG5hbWVfbGVuZ3RoKSkKCgojIGRyb3AgbWlzc2luZyB2YWx1ZXMKcHJpY2VzX2RmIDwtIHByaWNlc19kZiAlPiUgCiAgZHJvcF9uYSgpCgoKCmBgYAoKCiMgRXhwbG9yYXRvcnkgQW5hbHlzaXMKCiMjIFRvdGFsIE51bWJlciBvZiBCb29raW5ncyBwZXIgQm9yb3VnaAoKYGBge3J9CiMgbnVtYmVyIG9mIGJvb2tpbmdzIHBlciBib3JvdWdoCnByaWNlc19kZiAlPiUgCiAgIyBncm91cCBieSBuZWlnaGJvdXJob29kCiAgZ3JvdXBfYnkobmVpZ2hib3VyaG9vZF9ncm91cCkgJT4lIAogICMgcmV0dXJuIHRvdGFsIGJvb2tpbmdzIGZvciBlYWNoIG5laWdoYm91cmhvb2QKICBzdW1tYXJpc2UobnVtX2Jvb2tpbmdzID0gbigpKSAlPiUgCiAgIyBhcnJhbmdlIGZyb20gaGlnaGVzdCB0byBsb3dlc3QKICBhcnJhbmdlKGRlc2MobnVtX2Jvb2tpbmdzKSkgJT4lIAogICMgY3JlYXRlIGEgYmFyIGNoYXJ0IHRvIHZpc3VhbGlzZSByZXN1bHQgCiAgZ2dwbG90KGFlcyhyZW9yZGVyKG5laWdoYm91cmhvb2RfZ3JvdXAsIG51bV9ib29raW5ncyksIG51bV9ib29raW5ncywgCiAgICAgICAgICAgICBmaWxsID0gbmVpZ2hib3VyaG9vZF9ncm91cCkpICsKICAjIHNwZWNpZnkgYmFyIGNoYXJ0CiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICMgYW5ub3RhdGUgZWFjaCBiYXIKICBnZW9tX2xhYmVsKG1hcHBpbmcgPSBhZXMobGFiZWwgPSBudW1fYm9va2luZ3MpLCBzaXplID0gMywgCiAgICAgICAgICAgICBmaWxsID0gIiNGNUZGRkEiLCBmb250ZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpICsKICAjIGFkZCB0aGVtZQogIHRoZW1lX2NsYXNzaWMoKSArCiAgIyB0aXRsZXMKICBsYWJzKHggPSAiQm9yb3VnaCIsIHkgPSAiTnVtYmVyIG9mIEJvb2tpbmdzIiwgCiAgICAgICB0aXRsZSA9ICJUb3RhbCBOdW1iZXIgb2YgQm9va2luZ3MgcGVyIEJvcm91Z2giKQpgYGAKTWFuaGF0dGFuIGFuZCBCcm9va2x5biBib3RoIGJ5IGZhciB0aGUgbW9zdCBwb3B1bGFyIGFyZWFzIGZvciBBaXJibmIgbGlzdGluZ3MuCgpUd28gY2VudHJhbCBCb3JvdWdocyB3aGljaCBtYXkgaW5kaWNhdGUgdGhlIG1haW4gcmVhc29uIHBlb3BsZSBib29rIGlzIGZvciAKaG9saWRheXMgLyBUb3VyaXNtLiAKCgojIyBBdmVyYWdlIFByaWNlIHBlciBOZWlnaGJvdXJob29kIEdyb3VwCgpgYGB7cn0KCiMgQXZlcmFnZSBQcmljZSBwZXIgUm9vbQpwcmljZV9wZXJfcm9vbSA8LSBwcmljZXNfZGYgJT4lIAogICMgZ3JvdXAgYnkgcm9vbV90eXBlCiAgZ3JvdXBfYnkocm9vbV90eXBlKSAlPiUgCiAgIyByZXR1cm4gYXZlcmFnZSBwcmljZSBwZXIgcm9vbV90eXBlCiAgc3VtbWFyaXNlKGF2Z19wcmljZSA9IG1lYW4ocHJpY2UpKSAlPiUgCiAgIyBjcmVhdGUgYmFyIGNoYXJ0IHRvIHZpc3VhbGlzZSByZXN1bHQKICBnZ3Bsb3QoYWVzKHJvb21fdHlwZSwgYXZnX3ByaWNlLCBmaWxsID0gcm9vbV90eXBlKSkgKwogICMgc3BlY2lmeSBiYXIgY2hhcnQKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgIyBhbm5vdGF0ZSBlYWNoIGJhcgogIGdlb21fbGFiZWwobWFwcGluZyA9IGFlcyhsYWJlbCA9IHJvdW5kKGF2Z19wcmljZSwgMikpLCBzaXplID0gNiwgCiAgICAgICAgICAgICBmaWxsID0gIiNGNUZGRkEiLCBmb250ZmFjZSA9ICJib2xkIiwgCiAgICAgICAgICAgICAjIHBvc2l0aW9uIGNoYW5nZSB0byBtYWtlIHN1cmUgbGFiZWwgc3RheXMgb24gcGFnZQogICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuOSkpICsKICAjIGFkZCB0aGVtZQogIHRoZW1lX2NsYXNzaWMoKSArCiAgIyB0aXRsZXMKICBsYWJzKHggPSAiUm9vbSBUeXBlIiwgeSA9ICJBdmVyYWdlIFByaWNlICgkKSIsIAogICAgICAgdGl0bGUgPSAiQXZlcmFnZSBQcmljZSBieSBSb29tIFR5cGUiKQoKCiMgQXZlcmFnZSBwcmljZSBwZXIgbmVpZ2hib3VyaG9vZCBncm91cApwcmljZV9yb29tX2Jvcm91Z2ggPC0gcHJpY2VzX2RmICU+JSAKICAjIGdyb3VwIGJ5IGJvdGggbmVpZ2hib3VyaG9vZCBhbmQgcm9vbV90eXBlCiAgZ3JvdXBfYnkobmVpZ2hib3VyaG9vZF9ncm91cCwgcm9vbV90eXBlKSAlPiUgCiAgIyByZXR1cm4gYXZlcmFnZSBwcmljZSBmb3IgZWFjaCByb29tIHR5cGUgaW4gZWFjaCBuZWlnaGJvdXJob29kCiAgc3VtbWFyaXNlKGF2Z19wcmljZSA9IG1lYW4ocHJpY2UpKSAlPiUgCiAgIyBzb3J0IGZyb20gaGlnaGVzdCB0byBsb3dlc3QgcHJpY2UKICBhcnJhbmdlKGRlc2MoYXZnX3ByaWNlKSkgJT4lIAogICMgY3JlYXRlIGJhciBjaGFydAogIGdncGxvdChhZXMocmVvcmRlcihuZWlnaGJvdXJob29kX2dyb3VwLCBhdmdfcHJpY2UpLCBhdmdfcHJpY2UsIAogICAgICAgICAgICAgZmlsbCA9IHJvb21fdHlwZSkpICsKICAjIHNwZWNpZnkgYmFyIGNoYXJ0CiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICAjIGFubm90YXRlIGJhcnMKICBnZW9tX2xhYmVsKG1hcHBpbmcgPSBhZXMobGFiZWwgPSByb3VuZChhdmdfcHJpY2UsIDIpKSwgc2l6ZSA9IDIuNSwgCiAgICAgICAgICAgICBmaWxsID0gIiNGNUZGRkEiLCBmb250ZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUsIAogICAgICAgICAgICAgIyBwb3NpdGlvbiBjaGFuZ2UgdG8gbWFrZSBzdXJlIGxhYmVsIHN0YXlzIG9uIHBhZ2UKICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjkpKSArCiAgIyB0aGVtZQogIHRoZW1lX2NsYXNzaWMoKSArCiAgIyB0aXRsZXMKICBsYWJzKHggPSAiTmV3IFlvcmsgQm9yb3VnaHMiLCB5ID0gIkF2ZXJhZ2UgUHJpY2UgKCQpIiwgCiAgICAgICB0aXRsZSA9ICJBdmVyYWdlIFByaWNlIHBlciBCb3JvdWdoIGJ5IFJvb20gVHlwZSIpICsgCiAgIyBzcGxpdCBpbnRvIHJvb21fdHlwZXMKICBmYWNldF93cmFwKH5yb29tX3R5cGUpICsKICAjIGZsaXAgeCBhbmQgeSBheGlzCiAgY29vcmRfZmxpcCgpCgojIHBsb3QgYm90aCB2aXN1YWxpc2F0aW9ucyB0b2dldGhlcgpjb3dwbG90OjpwbG90X2dyaWQocHJpY2VfcGVyX3Jvb20sIHByaWNlX3Jvb21fYm9yb3VnaCwgbnJvdyA9IDIpCmBgYAoKCiMjIFRoZSAxMCBtb3N0IGV4cGVuc2l2ZSBhbmQgY2hlYXBlc3QgTmV3IFlvcmsgRGlzdHJpY3RzCgpgYGB7cn0KIyBUb3AgMTAgbW9zdCBleHBlbnNpdmUgZGlzdHJpY3RzIG9uIGF2ZXJhZ2UKYSA8LSBwcmljZXNfZGYgJT4lIAogICMgZ3JvdXAgYnkgaW5kaXZpZHVhbCBkaXN0cmljdHMgd2l0aGluIG5laWdoYm91cmhvb2RzCiAgZ3JvdXBfYnkobmVpZ2hib3VyaG9vZCkgJT4lIAogICMgcmV0dXJuIGF2ZXJhZ2UgcHJpY2UKICBzdW1tYXJpc2UoYXZnX3ByaWNlID0gbWVhbihwcmljZSkpICU+JSAKICAjIHNvcnQgZnJvbSBoaWdoZXN0IHRvIGxvd2VzdAogIGFycmFuZ2UoZGVzYyhhdmdfcHJpY2UpKSAlPiUgCiAgIyByZXR1cm4gdGhlIHRvcCAxMCAob3V0IG9mIDIxOCkKICBzbGljZSgxOjEwKSAlPiUgCiAgIyBjcmVhdGUgYmFyIHBsb3QKICBnZ3Bsb3QoYWVzKHJlb3JkZXIobmVpZ2hib3VyaG9vZCwgYXZnX3ByaWNlKSwgYXZnX3ByaWNlLCAKICAgICAgICAgICAgIGZpbGwgPSBuZWlnaGJvdXJob29kKSkgKyAKICAjIHNwZWNpZnkgYmFyIGNoYXJ0CiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICMgYW5ub3RhdGUgYmFycyAKICBnZW9tX2xhYmVsKG1hcHBpbmcgPSBhZXMobGFiZWwgPSByb3VuZChhdmdfcHJpY2UsIDIpKSwgc2l6ZSA9IDMsIAogICAgICAgICAgICAgZmlsbCA9ICIjRjVGRkZBIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICAjIHRoZW1lCiAgdGhlbWVfY2xhc3NpYygpICsKICAjIGNoYW5nZSB4LWF4aXMgbGFiZWwgcG9zaXRpb24KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDMwLCB2anVzdCA9IDAuOTUsIGhqdXN0ID0gMSkpICsKICAjIHRpdGxlcwogIGxhYnMoeCA9ICJOZWlnaGJvdXJob29kIiwgeSA9ICJBdmVyYWdlIFByaWNlICgkKSIsIAogICAgICAgdGl0bGUgPSAiVGhlIDEwIE1vc3QgRXhwZW5zaXZlIERpc3RyaWN0cyBvbiBBdmVyYWdlIikKCiMgdG9wIDEwIGxlYXN0IGV4cGVuc2l2ZSBkaXN0cmljdHMgb24gYXZlcmFnZQpiIDwtIHByaWNlc19kZiAlPiUgCiAgIyBncm91cCBieSBpbmRpdmlkdWFsIGRpc3RyaWN0cyB3aXRoaW4gbmVpZ2hib3VyaG9vZHMKICBncm91cF9ieShuZWlnaGJvdXJob29kKSAlPiUgCiAgIyByZXR1cm4gYXZlcmFnZSBwcmljZXMKICBzdW1tYXJpc2UoYXZnX3ByaWNlID0gbWVhbihwcmljZSkpICU+JSAKICAjIGFycmFuZ2UgZnJvbSBsb3dlc3QgdG8gaGlnaGVzdAogIGFycmFuZ2UoYXZnX3ByaWNlKSAlPiUgCiAgIyByZXR1cm4gYm90dG9tIDEwIHByaWNlcwogIHNsaWNlKDE6MTApICU+JSAKICAjIGNyZWF0ZSBiYXIgY2hhcnQKICBnZ3Bsb3QoYWVzKHJlb3JkZXIobmVpZ2hib3VyaG9vZCwgYXZnX3ByaWNlKSwgYXZnX3ByaWNlLCAKICAgICAgICAgICAgIGZpbGwgPSBuZWlnaGJvdXJob29kKSkgKyAKICAjIHNwZWNpZnkgYmFyIGNoYXJ0CiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICMgYW5ub3RhdGUgYmFycwogIGdlb21fbGFiZWwobWFwcGluZyA9IGFlcyhsYWJlbCA9IHJvdW5kKGF2Z19wcmljZSwgMikpLCBzaXplID0gMywgCiAgICAgICAgICAgICBmaWxsID0gIiNGNUZGRkEiLCBmb250ZmFjZSA9ICJib2xkIikgKwogICMgdGhlbWUKICB0aGVtZV9jbGFzc2ljKCkgKwogICMgY2hhbmdlIHgtYXhpcyBsYWJlbCBwb3NpdGlvbgogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIHZqdXN0ID0gMC45NSwgaGp1c3QgPSAxKSkgKwogICMgdGl0bGVzCiAgbGFicyh4ID0gIk5laWdoYm91cmhvb2QiLCB5ID0gIkF2ZXJhZ2UgUHJpY2UgKCQpIiwgCiAgICAgICB0aXRsZSA9ICJUaGUgMTAgTGVhc3QgRXhwZW5zaXZlIERpc3RyaWN0cyBvbiBBdmVyYWdlIikKCiMgYWRkIHBsb3RzIGludG8gdGhlIHNhbWUgb3V0cHV0CmNvd3Bsb3Q6OnBsb3RfZ3JpZChhLCBiLCBucm93PTIpCmBgYAoKVGhlIDEwIG1vc3QgZXhwZW5zaXZlIGRpc3RyaWN0cyBhcmUgYWxsIGxvY2F0ZWQgaW4gTWFuaGF0dGFuIGFwYXJ0IGZyb20sIApOZXBvbnNpdCAoUXVlZW5zKSBhbmQgV2lsbG93QnJvb2sgKFN0YXRlbiBJc2xhbmQpCgpUaGUgbWFqb3JpdHkgb2YgdGhlIDEwIGxlYXN0IGV4cGVuc2l2ZSBkaXN0cmljdHMgcmVzaWRlIGluIHRoZSBCcm9ueCwgU3RhdGUgCklzbGFuZCBhbmQgUXVlZW5zCgojIyBBdmVyYWdlIFJldmlld3MgYnkgTGFzdCBNb250aCBSZXZpZXcgU3VibWl0dGVkCgpgYGB7cn0KIyBBdmVyYWdlIFJldmlld3MgcGVyIE1vbnRoIGJ5IExhc3QgTW9udGggcmV2aWV3IHdhcyBsZWZ0CnByaWNlc19kZiAlPiUgCiAgIyBjaGFuZ2UgbGFzdF9yZXZpZXdfbW9udGggdG8gYSBmYWN0b3IgCiAgbXV0YXRlKGxhc3RfcmV2aWV3X21vbnRoID0gYXNfZmFjdG9yKGxhc3RfcmV2aWV3X21vbnRoKSkgJT4lIAogICMgZ3JvdXAgYnkgbGFzdF9yZXZpZXdfbW9udGgKICBncm91cF9ieShsYXN0X3Jldmlld19tb250aCkgJT4lIAogICMgcmV0dXJuIHRoZSBhdmVyYWdlX3Jldmlld3NfcGVyX21vbnRoIAogIHN1bW1hcmlzZShuID0gbWVhbihyZXZpZXdzX3Blcl9tb250aCkpICU+JSAKICAjIGNyZWF0ZSBhIGJhciBncmFwaAogIGdncGxvdChhZXMobGFzdF9yZXZpZXdfbW9udGgsIG4sIGZpbGwgPSBsYXN0X3Jldmlld19tb250aCkpICsgCiAgIyBzcGVjaWZ5IGJhciBncmFwaAogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICAjIHRoZW1lCiAgdGhlbWVfYncoKSArCiAgIyB0aXRsZXMKICBsYWJzKHggPSAiTGFzdCBSZXZpZXcgTW9udGgiLCB5ID0gIkF2ZXJhZ2UgUmV2aWV3cyBwZXIgTW9udGgiLCAKICAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgUmV2aWV3cyBieSBMYXN0IE1vbnRoIFJldmlldyBTdWJtaXR0ZWQiKQpgYGAKClN1Z2dlc3RzIHRoYXQgbW9zdCBwZW9wbGUgYXJlIGxlYXZpbmcgcmV2aWV3cyBpbiB0aGUgc3VtbWVyLCBpbmRpY2F0aW5nIHNvbWUKc2Vhc29uYWxpdHkgdG8gQWlyYm5iIGJvb2tpbmcgaW4gTmV3IFlvcmsuIAoKCiMgUHJpY2UgRGVuc2l0eSBieSBBcmVhCgpgYGB7cn0KIyBwcmljZSBkZW5zaXR5IGJ5IE5ldyBZb3JrIEJvcm91Z2gKZ2dwbG90KAogICMgY3JlYXRlIHBsb3QgZm9yIHByaWNlIGxlc3MgdGhhbiAkNTAwCiAgc3Vic2V0KHByaWNlc19kZiwgcHJpY2UgPCA1MDApLGFlcyh4ID0gcHJpY2UpKSArCiAgIyBzcGVjaWZ5IGRlbnNpdHkgcGxvdAogIGdlb21fZGVuc2l0eSgKICAgIG1hcHBpbmcgPSBhZXMoZmlsbCA9IG5laWdoYm91cmhvb2RfZ3JvdXApLCAKICAgIGJhbmR3aWR0aCA9IDEwMCwgYWxwaGEgPSAxLCBzaXplID0gMC41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgIyB0aGVtZQogIHRoZW1lX2J3KCkgKwogICMgc2hvdyBpbmRpdmlkdWFsIGRlbnNpdHkgcGxvdHMgZm9yIGJvcm91Z2hzCiAgZmFjZXRfd3JhcCh+bmVpZ2hib3VyaG9vZF9ncm91cCkgKwogICMgdGl0bGVzCiAgbGFicyh4ID0gIlByaWNlIiwgeSA9ICJEZW5zaXR5IiwgdGl0bGUgPSAiUHJpY2UgRGVuc2l0eSBieSBCb3JvdWdoIikKYGBgCgpQcmljaW5nIGRlbnNpdHkgcGxvdCByZXZlYWxzIHRoYXQgYm9yb3VnaHMgd2l0aCBmZXdlciBhbW91bnQgb2YgYm9va2luZ3MgKFF1ZWVucywKU3RhdGVuIElzbGFuZCBhbmQgQnJvbngpIGhhdmUgYSBoaWdoZXIgZGVuc2l0eSBvZiBsb3dlciBwcmljZXMgCgpNb3N0IGNvbW1vbiBhcmVhcyAoTWFoYXR0YW4gYW5kIEJyb29rbHluKSBoYXZlIGEgd2lkZXIgZGVuc2l0eSBwbG90IGluZGljYXRpbmcgCnRoYXQgcHJpY2VzIHZhcnkgbW9yZS4gCgoKIyBUZXh0IE1pbmluZwoKSSB3YW50IHRvIGZpbmQgdGhlIHdvcmRzIHRoYXQgYXJlIGFzc29jaWF0ZWQgd2l0aCBkaWZmZXJlbnQgcHJpY2UgcmFuZ2VzLiAKClNvIEkgbmVlZCB0byBjcmVhdGUgbmV3IHZhcmlhYmxlcyB3aGljaCBjbGFzc2lmeSB0aGUgcHJpY2UgcmFuZ2Ugb2YgZWFjaAphaXJibmIKCndlIHdpbGwgZGVmaW5lIHByaWNlIHJhbmdlcyBiYXNlZCBhcm91bmQgdGhlIGF2ZXJhZ2UgcHJpY2UgZm9yIHRvdGFsIGJvb2tpbmdzCgpgYGB7cn0KIyByZXR1cm4gbWVhbl9wcmljZSBvZiBBaXJibmJzCnByaWNlc19kZiAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fcHJpY2UgPSBtZWFuKHByaWNlKSkKYGBgCm1lYW4gcHJpY2UgaXMgJDE0MiB0aGVyZWZvcmUsIGxvdyB3aWxsIGJlIGxlc3MgdGhhbiAkMTAwLCBtZWRpdW0gd2lsbCBiZSAKYmV0d2VlbiAkMTAwIC0gJDIwMCwgaGlnaCB3aWxsIGJlICQyMDAgLSAkMzAwIGFuZCB2ZXJ5IGhpZ2ggd2lsbCBiZSBncmVhdGVyIHRoYW4KJDMwMAoKYGBge3J9CiMgY3JlYXRlIG5ldyBjYXRlZ29yaWNhbCB2YXJpYWJsZSBmb3IgcHJpY2UKcHJpY2VzX25ldyA8LSBwcmljZXNfZGYgJT4lIAogIG11dGF0ZShwcmljZV9jbGFzcyA9IGNhc2Vfd2hlbigKICAgIHByaWNlIDwgMTAwIH4gIkxvdyIsIAogICAgcHJpY2UgPCAyMDAgfiAiTWVkaXVtIiwgCiAgICBwcmljZSA8IDMwMCB+ICJIaWdoIiwKICAgIFRSVUUgfiAiVmVyeSBIaWdoIgogICkpCiAgCmBgYAoKCgojIyBIaWdoL1ZlcnkgSGlnaCBQcmljZSBSYW5nZQoKCmBgYHtyfQojIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBoaWdoIHByaWNlcwpoaWdoX3ByaWNlX3dvcmRzIDwtIHByaWNlc19uZXcgJT4lIAogICMgZmlsdGVyIGZvciBIaWdoIGFuZCBWZXJ5IEhpZ2ggcHJpY2UgY2xhc3NlcyAKICBmaWx0ZXIocHJpY2VfY2xhc3MgJWluJSBjKCJIaWdoIiwgIlZlcnkgSGlnaCIpKSAlPiUgCiAgIyB0YWtlIGluZGl2aWR1YWwgd29yZHMgZnJvbSB0aGUgbmFtZSBjb2x1bW4KICB1bm5lc3RfdG9rZW5zKHdvcmQsIG5hbWUpICU+JSAKICAjIHRha2Ugb3V0IHN0b3Bfd29yZHMgCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSAKICAjIHNlbGVjdCB3b3JkIGNvbHVtbgogIGRwbHlyOjpzZWxlY3Qod29yZCkKCiMgbW9zdCBjb21tb24gd29yZHMKc29ydGVkX2hwX3dvcmRzIDwtIGhpZ2hfcHJpY2Vfd29yZHMgJT4lCiAgIyBzb3J0IG1vc3QgY29tbW9uIHdvcmRzCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCgojIGNyZWF0ZSB3b3JkIGNsb3VkIGZvciBoaWdoL3ZlcnkgaGlnaCBwcmljZSBBaXJibmJzCndvcmRjbG91ZCgKICAjIHdvcmRzIHRvIHVzZSBmb3Igd29yZCBjbG91ZCAKICB3b3JkcyA9IHNvcnRlZF9ocF93b3JkcyR3b3JkLAogICMgbnVtYmVyIG9mIHRpbWVzIHRoZXNlIHdvcmRzIGFwcGVhcgogIGZyZXEgPSBzb3J0ZWRfaHBfd29yZHMkbiwgCiAgIyBtYXhpbXVtIG51bWJlciBvZiB3b3JkcyB1c2VkCiAgbWF4LndvcmRzPTYwLCAKICAjIG5vIHJhbmRvbSBvcmRlcgogIHJhbmRvbS5vcmRlcj1GQUxTRSwgCiAgIyBwcm9wb3J0aW9uIG9mIHdvcmRzIHdpdGggYSA5MCBkZWdyZWUgYW5nbGUKICByb3QucGVyPTAuMzUsIAogICMgYWRkIGNvbG91ciBwYWxldHRlCiAgY29sb3JzPWJyZXdlci5wYWwoOCwgIlNwZWN0cmFsIikpCgpgYGAKCgpXb3JkcyB0aGF0IHN0YW5kIG91dCBmcm9tIHdvcmRjbG91ZCBpbmNsdWRlOiBiZWRyb29tLCBhcGFydG1lbnQsIHZpbGxhZ2UsIGx1eHVyeSwgCmxvY2F0aW9uLCBNYW5oYXR0YW4sIHNwYWNpb3VzLCBwYXJrCi0gc3RhcnQgdG8gdW5kZXJzdGFuZCB3aGF0IGtpbmQgb2YgQWlyYm5icyBhcmUgYmVpbmcgYWR2ZXJ0aXNlZCBmb3IgaGlnaCBwcmljZXMKCgojIyBMb3cgUHJpY2UgUmFuZ2UKCgpgYGB7cn0KIyB3b3JkcyBhc3NvY2lhdGVkIHdpdGggbG93IHByaWNlcwpsb3dfcHJpY2Vfd29yZHMgPC0gcHJpY2VzX25ldyAlPiUgCiAgIyBmaWx0ZXIgZm9yIHJvd3MgYXNzb2NpYXRlZCB3aXRoIGxvdyBwcmljZXMKICBmaWx0ZXIocHJpY2VfY2xhc3MgJWluJSAiTG93IikgJT4lIAogICMgdGFrZSB3b3JkcyBmcm9tIG5hbWUgY29sdW1uCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBuYW1lKSAlPiUgCiAgIyByZW1vdmUgc3RvcCB3b3JkcwogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUgCiAgIyBzZWxlY3Qgd29yZCBjb2x1bW4KICBkcGx5cjo6c2VsZWN0KHdvcmQpCgojIG1vc3QgY29tbW9uIHdvcmRzCnNvcnRlZF9scF93b3JkcyA8LSBsb3dfcHJpY2Vfd29yZHMgJT4lIAogICMgc29ydCBtb3N0IGNvbW1vbiB3b3JkcwogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQoKd29yZGNsb3VkKAogICMgc2VsZWN0IHdvcmRzIAogIHdvcmRzID0gc29ydGVkX2xwX3dvcmRzJHdvcmQsIAogICMgbnVtYmVyIG9mIHRpbWVzIHRoZXNlIHdvcmRzIGFwcGVhcgogIGZyZXEgPSBzb3J0ZWRfbHBfd29yZHMkbiwgCiAgIyBtYXhpbXVtIG51bWJlciBvZiB3b3JkcyBpbiBjbG91ZAogIG1heC53b3Jkcz02MCwgCiAgIyBubyByYW5kb20gb3JkZXIKICByYW5kb20ub3JkZXI9RkFMU0UsIAogICMgcHJvcG9ydGlvbiBvZiB3b3JkcyByb3RhdGVkCiAgcm90LnBlcj0wLjM1LCAKICAjIGNvbG91ciBwYWxldHRlCiAgY29sb3JzPWJyZXdlci5wYWwoOCwgIlNwZWN0cmFsIikpCmBgYAoKd2hlbiB3ZSBsb29rIGF0IHRoZSB3b3JkIGNsb3VkIG9mIHRoZSBsb3cgcHJpY2UgcmFuZ2UsIHdlIHNlZSBzb21lIHNpbWlsYXJpdGllcwp3aXRoIHRoZSBoaWdoIHByaWNlIHJhbmdlIGluZGljYXRpbmcgb3duZXJzIGFyZSB0cnlpbmcgdG8gc2VsbCB0aGUgcHJvcGVydHkgYXMKdXAgbWFya2V0LiAKCkJpZyBlbXBoYXNpcyBvbiBwcm9wZXJ0eSBiZWluZyAicHJpdmF0ZSIgd2hpY2ggaXMgbGlrZWx5IHRvIGJlIGEgYmlnIGNvbmNlcm4KZm9yIHBlb3BsZSB3aGVuIG5vdCBwYXlpbmcgdGhhdCBtdWNoLiBJbiBjb250cmFzdCwgcHJpdmFjeSBpcyBhIGdpdmVuIHdoZW4gcGF5aW5nCmZvciBoaWdoIGVuZCBhY2NvbW1vZGF0aW9uLiAKCgoKIyMgTWVkaXVtIFByaWNlIFJhbmdlCgpgYGB7cn0KIyB3b3JkcyBhc3NvY2lhdGVkIHdpdGggbWVkaXVtIHByaWNlIHJhbmdlIChhcm91bmQgdGhlIGF2ZXJhZ2UpCm1lZGl1bV9wcmljZV93b3JkcyA8LSBwcmljZXNfbmV3ICU+JSAKICAjIGZpbHRlciByb3dzIGFzc29jaWF0ZWQgd2l0aCBtZWRpdW0gcHJpY2UgcmFuZ2UKICBmaWx0ZXIocHJpY2VfY2xhc3MgJWluJSAiTWVkaXVtIikgJT4lIAogICMgc2VsZWN0IHdvcmRzIGZyb20gbmFtZSBjb2x1bW4KICB1bm5lc3RfdG9rZW5zKHdvcmQsIG5hbWUpICU+JSAKICAjIHJlbW92ZSBzdG9wIHdvcmRzCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSAKICAjIHNlbGVjdCB3b3JkcwogIGRwbHlyOjpzZWxlY3Qod29yZCkKCgojIG1vc3QgY29tbW9uIHdvcmRzCnNvcnRlZF9tcF93b3JkcyA8LSBtZWRpdW1fcHJpY2Vfd29yZHMgJT4lIAogICMgc29ydCB3b3JkcwogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQoKCiMgd29yZCBjbG91ZCBmb3IgbWVkaXVtIHByaWNlIHJhbmdlCndvcmRjbG91ZCgKICAjIHdvcmRzIHRvIHVzZQogIHdvcmRzID0gc29ydGVkX21wX3dvcmRzJHdvcmQsIAogICMgZnJlcXVlbmN5IHdvcmRzIGFwcGVhcgogIGZyZXEgPSBzb3J0ZWRfbXBfd29yZHMkbiwgCiAgIyBtYXhpbXVtIG51bWJlciBvZiB3b3JkcwogIG1heC53b3Jkcz02MCwgCiAgIyByYW5kb20gb3JkZXIgPSBGQUxTRSAtLT4gbWFrZXMgaXQgbmVhdAogIHJhbmRvbS5vcmRlcj1GQUxTRSwgCiAgIyBwcm9wb3J0aW9uIG9mIHdvcmRzIHJvdGF0ZWQKICByb3QucGVyPTAuMzUsIAogICMgY29sb3VyIHBhbGV0dGUKICBjb2xvcnM9YnJld2VyLnBhbCg4LCAiU3BlY3RyYWwiKSkKYGBgCgpOb3QgYSB0cmVtZW5kb3VzIGFtb3VudCBvZiBkaWZmZXJlbmNlIGhlcmUsIHByb2JhYmx5IGFzIGV4cGVjdGVkIGl0IHRha2VzIGEgCmJhbGFuY2UgYmV0d2VlbiBsb3cgYW5kIGhpZ2ggcHJpY2UgcmFuZ2VzIGhpZ2hsaWdodGluZyBwcml2YWN5IGFzIGltcG9ydGFudCBidXQgCmFsc28gbW9yZSBlbXBoYXNpcyBvbiBsb2NhdGlvbi4gCgoKIyBNb3N0IFBvcHVsYXIgQmlncmFtcyB1c2VkIGluIEFpcmJuYiBOYW1lCgpBbmFseXNpcyBvZiB0aGUgbW9zdCBwb3B1bGFyIHdvcmQgY29tYmluYXRpb25zCgpgYGB7cn0KIyBPYnRhaW4gdGhlIDIgd29yZCBiaWdyYW1zCmJpZ3JhbV9uYW1lcyA8LSBwcmljZXNfbmV3ICU+JSAKICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgbmFtZSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpCgojIFNlcGFyYXRlIGludG8gdHdvIHdvcmRzCmJpZ3JhbXNfc2VwYXJhdGVkIDwtIGJpZ3JhbV9uYW1lcyAlPiUgCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpCgojIGZpbHRlciB0aGUgd29yZHMgZm9yIHN0b3Agd29yZHMKYmlncmFtc19maWx0ZXJlZCA8LSBiaWdyYW1zX3NlcGFyYXRlZCAlPiUgCiAgIyBmaWx0ZXIgd29yZHMgTk9UIElOIHN0b3Agd29yZHMKICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUgCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkKCiMgY291bnQgYW5kIHNvcnQgdGhlIG1vc3QgcG9wdWxhciB3b3JkcwpiaWdyYW1zX2NvdW50cyA8LSBiaWdyYW1zX2ZpbHRlcmVkICU+JSAKICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKQoKIyBtb3N0IHBvcHVsYXIgYmlncmFtcyBncmFwaApiaWdyYW1zX2NvdW50cyAlPiUgCiAgIyBmaWx0ZXIgb3V0IG51bWJlcnMKICBmaWx0ZXIoIXN0cl9kZXRlY3Qod29yZDEsICJbMC05XSIpKSAlPiUgCiAgIyB1bml0ZSBib3RoIHdvcmQgY29sdW1ucwogIHVuaXRlKGNvbCA9ICJiaWdyYW1zIiwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiLCByZW1vdmUgPSBUUlVFKSAlPiUgCiAgIyBnZXQgdG9wIDEwCiAgc2xpY2UoMToxMCkgJT4lIAogICMgdmlzdWFsaXNlCiAgZ2dwbG90KGFlcyhyZW9yZGVyKGJpZ3JhbXMsIG4pLCBuLCBmaWxsID0gYmlncmFtcykpICsgCiAgIyBzcGVjaWZ5IGJhciBncmFwaAogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICAjIGZsaXAgeCBhbmQgeS1heGlzCiAgY29vcmRfZmxpcCgpICsKICAjIHRoZW1lCiAgdGhlbWVfYncoKSArIAogICMgYW5ub3RhdGUgZWFjaCBiYXIKICBnZW9tX2xhYmVsKG1hcHBpbmcgPSBhZXMobGFiZWwgPSBuKSwgc2l6ZSA9IDMsIAogICAgICAgICAgICAgZmlsbCA9ICIjRjVGRkZBIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICAjIHRpdGxlcwogIGxhYnMoeSA9ICJOdW1iZXIgb2YgQmlncmFtIE1lbnRpb25zIiwgeCA9ICJCaWdyYW0iLCAKICAgICAgIHRpdGxlID0gIk1vc3QgUG9wdWxhciBCaWdyYW1zIGluIEFpcmJuYiBOYW1lIikKCmBgYAoKSG9zdHMgZW1waGFzaXMgb24gbG9jYXRpb24gaXMgYSBjb21tb24gc3RyYXRlZ3kgdG8gZW50aWNlIGNvbnN1bWVycwoKY29uc3VtZXJzIHdhbnQgdG8gYmUgY2xvc2VyIHRvIGZhbW91cyBsYW5kbWFya3MuIAoKCiMgR2VvLXNwYXRpYWwgQW5hbHlzaXMKCgpDYW4gd2Ugc2VlIGlmIHRoZSBnZW8tc3BhdGlhbCBiYWNrcyB1cCB0aGUgd29yZCBjbG91ZCBhbmQgYmlncmFtIGFuYWx5c2lzIG9mIAplbXBoYXNpcyBvbiBsb2NhdGlvbiBmb3IgbWVkaXVtIGFuZCBsb3cgcHJpY2UgcmFuZ2VzCgojIyBEZW5zaXR5IG9mIE5ldyBZb3JrIEJvcm91Z2hzCgoKYGBge3J9CiMgRGVuc2l0eSBvZiBOZXcgWW9yayBCb3JvdWdocwpwcmljZXNfbmV3ICU+JSAKICAjIHBsb3QgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZQogIGdncGxvdChhZXMobG9uZ2l0dWRlLCBsYXRpdHVkZSkpICsKICAjIHNwZWNpZnkgZGVuc2l0eSBwbG90IC0gaGFzIHRvIGJlIGdlb21fZGVuc2l0eTJkCiAgIyBzcGVjaWZ5IGRlbnNpdHkgYnkgbmVpZ2hib3VyaG9vZHMKICBnZW9tX2RlbnNpdHkyZChhZXMoY29sb3VyID0gbmVpZ2hib3VyaG9vZF9ncm91cCkpICsgCiAgIyB0aGVtZQogIHRoZW1lX2J3KCkgKwogICMgdGl0bGUKICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgb2YgQm9yb3VnaHMiKQpgYGAKCiMjIERlbnNpdHkgb2YgUHJpY2UgQ2xhc3MKCmBgYHtyfQojIERlbnNpdHkgb2YgUHJpY2UgQ2xhc3MKcHJpY2VzX25ldyAlPiUgCiAgIyBwbG90IGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUKICBnZ3Bsb3QoYWVzKGxvbmdpdHVkZSwgbGF0aXR1ZGUpKSArCiAgIyBzcGVjaWZ5IGRlbnNpdHkgYnkgcHJpY2VfY2xhc3MKICBnZW9tX2RlbnNpdHkyZChhZXMoY29sb3VyID0gcHJpY2VfY2xhc3MpKSArIAogICMgdGhlbWUKICB0aGVtZV9idygpICsKICAjIHRpdGxlcwogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBvZiBQcmljZSBDbGFzcyIpCgpgYGAKCgpjYW4gc2VlIHRoZSBzcHJlYWQgb2YgcHJpY2VzIGJhc2VkIG9uIHRoZSBhcmVhLCBjYW4gc2VlIHRoYXQgbG93ZXIgcHJpY2VzIAp0ZW5kIHRvIGxvY2F0ZWQgdG8gdGhlIHBlcmltZXRlciBvZiBOZXcgWW9yaywgaW5kaWNhdGluZyB0aGF0IGxvY2F0aW9uIHRvd2FyZHMKY2VudHJlIG9mIE1hbmhhdHRhbiBpcyBhIGxhcmdlIGRldGVybWluYW50IG9mIHByaWNlLgoKCiMjIExlYWZsZXQgTWFwCgpgYGB7cn0KIyBkZWZpbmUgY29sb3VyIHBhbGV0dGUgZm9yIG1hcAogcGFsIDwtIGNvbG9yRmFjdG9yKAogICAjIHNlbGVjdCBvd24gY29sb3VyIHNjaGVtZQogICBwYWxldHRlID0gYygib3JhbmdlIiwgIndoaXRlIiwgInllbGxvdyIsICJyZWQiKSwgCiAgICMgc2VsZWN0IHZhcmlhYmxlIGZvciBjb2xvdXIgcGFsZXR0ZQogICBkb21haW4gPSBwcmljZXNfZGYkcHJpY2VfY2xhc3MpCgojIGxlYWZsZXQgbWFwCmxlYWZsZXQoZGF0YSA9IHByaWNlc19uZXcpICU+JSAKICAjIGFkZCBkYXJrIGJhY2tncm91bmQgZm9yIG1hcAogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuRGFya01hdHRlck5vTGFiZWxzKSAlPiUgCiAgIyBhZGQgbWFya2VyIGZvciBlYWNoIEFpcmJuYiBiYXNlZCBvbiBwcmljZV9jbGFzcwogIGFkZENpcmNsZU1hcmtlcnMofmxvbmdpdHVkZSwgfmxhdGl0dWRlLCBjb2xvciA9IH5wYWwocHJpY2VfY2xhc3MpLCB3ZWlnaHQgPSAxLCAKICAgICAgICAgICAgICAgICAgIHJhZGl1cz0xLCBmaWxsT3BhY2l0eSA9IDAuMSwgb3BhY2l0eSA9IDAuMSkgJT4lCiAgIyBhZGQgbGVnZW5kIGZvciBjb2xvdXIgYXNzb2NpYXRlZCB3aXRoIHByaWNlX2NsYXNzCiAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbCA9IHBhbCwgdmFsdWVzID0gfnByaWNlX2NsYXNzLAogICAgICAgICAgICB0aXRsZSA9ICJQcmljZSBDbGFzcyIsCiAgICAgICAgICAgIG9wYWNpdHkgPSAxCiAgKSAKYGBgCgoKTGVhZmxldCBtYXAgc2hvd3MgdGhlIGNvbnRyYXN0IGJldHdlZW4gZXh0cmVtaXRpZXMgYW5kIGNpdHkgY2VudHJlLCBldmlkZW50bHkKbXVjaCBoaWdoZXIgcHJpY2VzIGluIHRoZSBjaXR5LiAKCl9fU3VtbWFyeV9fCgpQcmljZXMgYXJlIGJlaW5nIGltcGFjdGVkIGJ5OgotIGxvY2F0aW9uCi0gdHlwZSBvZiBhY2NvbW1vZGF0aW9uIAoKCiMgTW9kZWwgQnVpbGRpbmcgLSBMaW5lYXIgUmVncmVzc2lvbgoKQXBwcm9hY2g6IAotIG1hbnVhbAotIGdvb2Qgd2F5IHRvIGJlY29tZSBmYW1pbGlhciB3aXRoIGRhdGEKLSBhdm9pZCBvdmVyIG9yIHVuZGVyIGZpdHRpbmcgdGhlIG1vZGVsCgpHb2FscyBvZiB0aGUgbW9kZWw6IAoKLSBBIHdlbGwgZml0IG1vZGVsIHRoYXQgcmVhc29uYWJseSBleHBsYWlucyB2YXJpYW5jZSBpbiBwcmljZQotIHNhdGlzZmllcyBhc3N1bXB0aW9ucyBuZWVkZWQgdG8gdmFsaWRhdGUgbW9kZWwKLSBCZSBhYmxlIHRvIG91dHB1dCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGNvZWZmaWNpZW50cyBmb3IgY29uc3VtZXIvaG9zdApyZWNvbW1lbmRhdGlvbnMKCgojIyBEaXN0cmlidXRpb24gb2YgUHJpY2UKCkRlcGVuZGVudCB2YXJpYWJsZTogUHJpY2UKCkltcG9ydGFudCB0byBpbnZlc3RpZ2F0ZSBpdHMgZGlzdHJpYnV0aW9uIGFzIGl0IG1heSByZXF1aXJlIGEgdHJhbnNmb3JtYXRpb24gdG8gCmNyZWF0ZSBhIGJldHRlciBmaXQgZm9yIHRoZSBtb2RlbAoKCmBgYHtyfQojIGRpc3RyaWJ1dGlvbiBvZiBwcmljZSAKcHJpY2VzX25ldyAlPiUgCiAgIyBzZWxlY3QgcHJpY2UgZm9yIHgtYXhpcwogIGdncGxvdChhZXMocHJpY2UpKSArCiAgIyBzcGVjaWZ5IGhpc3RvZ3JhbQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDAsIGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBmaWxsID0gInJlZCIpICsgCiAgIyBhZGQgaW4gYSBkZW5zaXR5IHBsb3QKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIsIGZpbGwgPSAicmVkIikgKwogICMgdGhlbWUKICB0aGVtZV9idygpICsKICAjIHRpdGxlCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUHJpY2UiKQpgYGAKClByaWNlIGlzIGhlYXZpbHkgc2tld2VkIHRvIHRoZSByaWdodCBpbmRpY2F0ZXMgYSBsb2cgdHJhbnNmb3JtYXRpb24gaXMgbmVlZGVkCnRvIGdldCBhIG5vcm1hbCBiZWxsIHNoYXBlZCBkaXN0cmlidXRpb24KCmBgYHtyfQojIG1lYW4gcHJpY2UgCm1lYW5fcHJpY2UgPC0gcHJpY2VzX25ldyAlPiUgCiAgc3VtbWFyaXNlKG1fcHJpY2UgPSBtZWFuKHByaWNlKSkKCgojIGxvZyBiZWxsIHNoYXBlZCBkaXN0cmlidXRpb24gCnByaWNlc19uZXcgJT4lIAogICMgc2V0IHByaWNlIG9uIHgtYXhpcyBhcyBhIG5hdHVyYWwgbG9nYXJpdGhtCiAgZ2dwbG90KGFlcyhsb2cocHJpY2UpKSkgKwogICMgc3BlY2lmeSBoaXN0b2dyYW0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDAsIGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBmaWxsID0gInJlZCIpICsgCiAgIyBpbnNlcnQgZGVuc2l0eSBhcmVhCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yLCBmaWxsID0gInJlZCIpICsKICAjIGluc2VydCBsaW5lIHRvIGluZGljYXRlIGF2ZXJhZ2UKICBnZW9tX3ZsaW5lKGRhdGEgPSBtZWFuX3ByaWNlLCBhZXMoeGludGVyY2VwdCA9ICBsb2cobV9wcmljZSkpLCBzaXplID0gMSwgCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIpICsKICAjIHRoZW1lCiAgdGhlbWVfYncoKSArCiAgIyB0aXRsZQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGxvZyhwcmljZSkiKQpgYGAKCgoKIyMgRmluYWxpc2luZyBEYXRhIGZvciBNb2RlbAoKRHJvcHBlZCB2YXJpYWJsZXM6CgotICduYW1lJyAtIHVzZSBkdW1teSB2YXJpYWJsZXMgZm9yIGltcG9ydGFudCB3b3JkcyBhbmQgYmlncmFtcyBpbnN0ZWFkCi0gJ25laWdoYm91cmhvb2QnIC0gdXNlIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBmb3IgZml2ZSBOZXcgWW9yayBCb3JvdWdocwotICdwcmljZV9jbGFzcycgLSBoaWdoIGNvcnJlbGF0ZWQgd2l0aCBkZXBlbmRlbnQgdmFyaWFibGUgcHJpY2UKCkR1bW15IHZhcmlhYmxlcyBjcmVhdGVkIGZyb20gdGV4dCBhbmFseXNpczogCgotICdhcGFydG1lbnQnCi0gJ3ByaXZhdGUnCi0gJ2NlbnRyYWwgcGFyaycKCmBgYHtyfQpwcmljZXNfcmVnX2RmIDwtIHByaWNlc19uZXcgJT4lIAogICMgY3JlYXRlIGR1bW15IHZhcmlhYmxlcyBmb3IgY2hvc2VuIHdvcmRzIHRoYXQgbWF5IGltcGFjdCBwcmljZQogIG11dGF0ZShhcGFydG1lbnRfYWQgPSBpZl9lbHNlKHN0cl9kZXRlY3QobmFtZSwgIltBYV1wYXJ0bWVudCIpLCAiWUVTIiwgIk5PIiksCiAgICAgICAgIHByaXZhdGVfYWQgPSBpZl9lbHNlKHN0cl9kZXRlY3QobmFtZSwgIltQcF1yaXZhdGUiKSwgIllFUyIsICJOTyIpLAogICAgICAgICBjZW50cmFsX3BhcmtfYWQgPSBpZl9lbHNlKHN0cl9kZXRlY3QobmFtZSwgIltDY11lbnRyYWwgW1BwXWFyayIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWUVTIiwgIk5PIiksCiAgICAgICAgICkgJT4lIAogICMgcmVtb3ZlIHZhcmlhYmxlcyBub3QgY29uc2lkZXJlZCBmb3IgbW9kZWwKICBkcGx5cjo6c2VsZWN0KC1jKG5hbWUsIG5laWdoYm91cmhvb2QsIHByaWNlX2NsYXNzKSkgJT4lIAogICMgbG9nIHRyYW5zZm9ybSBwcmljZQogIG11dGF0ZShsb2dfcHJpY2UgPSBsb2cocHJpY2UgKyAxKSkgJT4lIAogICMgYnJpbmcgcHJpY2UgdG8gdGhlIGZyb250CiAgZHBseXI6OnNlbGVjdChsb2dfcHJpY2UscHJpY2UsIGV2ZXJ5dGhpbmcoKSkgJT4lIAogICMgY2hhbmdlIGFsbCBjaGFyYWN0ZXIgdmFyaWFibGVzIHRvIGZhY3RvcgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBpcy5jaGFyYWN0ZXIsIAogICAgICAgICAgICAgICAgLmZucyA9IGFzX2ZhY3RvcikpICU+JSAKICAjIHJlbW92ZSBvcmlnaW5hbCBwcmljZSB2YXJpYWJsZQogIGRwbHlyOjpzZWxlY3QoLXByaWNlKQogIApgYGAKCgpDaGVjayBhbGlhcygpIGZ1bmN0aW9uIHRvIGNoZWNrIGZvciBtdWx0aWNvbGxpbmVhcml0eSAKCmBgYHtyfQojIGNoZWNrIGZvciBtdWx0aWNvbGxpbmVhcml0eQphbGlhcyhsbShsb2dfcHJpY2UgfiAuLCBkYXRhID0gcHJpY2VzX3JlZ19kZikpCmBgYAoKRGF0YSBpcyBub3cgcmVhZHkgdG8gYmUgdXNlZCBpbiByZWdyZXNzaW9uIGFuYWx5c2lzCgoKClVzZSBnZ3BhaXJzKCkgdG8gaW52ZXN0aWdhdGUgd2hpY2ggdmFyaWFibGVzIGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIHByaWNlLgoKRGF0YSBzZXQgaXMgbGFyZ2Ugc28gcmVsYXRpb25zaGlwIGFuYWx5c2lzIHdpbGwgYmUgZGl2aWRlZCBpbnRvIG51bWVyaWMgYW5kIApub24tbnVtZXJpYyBkYXRhdHlwZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgc2VsZWN0IHZhcmlhYmxlcyB0aGF0IGFyZSBudW1lcmljCnZhcmlhYmxlX251bWVyaWMgPC0gcHJpY2VzX3JlZ19kZiAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykKCiMgZ2dwYWlycyB3aXRoIG9ubHkgbnVtZXJpYyB2YXJpYWJsZXMKZ2dwYWlycyh2YXJpYWJsZV9udW1lcmljKQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIHNlbGVjdCB2YXJpYWJsZXMgdGhhdCBhcmUgY2F0ZWdvcmljYWwgCnZhcmlhYmxlX25vbm51bWVyaWMgPC0gcHJpY2VzX3JlZ19kZiAlPiUKICAjIHVzZSBmdW5jdGlvbiB0byByZXR1cm4gdmFyaWFibGVzIHRoYXQgYXJlIGNhdGVnb3JpY2FsCiAgc2VsZWN0X2lmKGZ1bmN0aW9uKHgpICFpcy5udW1lcmljKHgpKQoKIyBuZWVkIHRvIGFkZCBsb2dfcHJpY2UgdG8gbm9uLW51bWVyaWMgZGF0YSAKdmFyaWFibGVfbm9ubnVtZXJpYyRsb2dfcHJpY2UgPC0gcHJpY2VzX3JlZ19kZiRsb2dfcHJpY2UKCiMgbm9uLW51bWVyaWMgZ2dwYWlycwpnZ3BhaXJzKHZhcmlhYmxlX25vbm51bWVyaWMpCmBgYAoKCiMjIFVuaXZhcmlhdGUgUmVncmVzc2lvbgoKTGFyZ2VzdCBjb3JyZWxhdGlvbiB3aXRoIHByaWNlOiBsb25naXR1ZGUKLSBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgYnkgMC4xNTUKLSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IHRoZSAwLjAwMSBsZXZlbCBvZiBzaWduaWZpY2FuY2UuIAoKVGhlIG5vbi1udW1lcmljIGdncGFpcnMgc3VnZ2VzdHMgdGhhdCBzZXZlcmFsIHZhcmlhYmxlcyB3aWxsIGJlIGFibGUgdG8gZXhwbGFpbiAKcHJpY2UgdmFyaWFuY2UuICAKCiMjIyByZWxhdGlvbnNoaXAgYmV0d2VlbiBsb2cocHJpY2UpIGFuZCBsb25naXR1ZGUKCmBgYHtyfQojIHNjYXR0ZXIgcGxvdCBmb3IgbG9nX3ByaWNlIH4gbG9uZ2l0dWRlCnByaWNlc19yZWdfZGYgJT4lIAogIGdncGxvdChhZXMobG9uZ2l0dWRlLCBsb2dfcHJpY2UpKSArIAogICMgc3BlY2lmeSBzY2F0dGVyIHBsb3QKICBnZW9tX3BvaW50KCkgKyAKICAjIGFkZCBpbiBsaW5lYXIgbW9kZWwgbGluZSBvZiBiZXN0IGZpdAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG91ciA9ICJyZWQiKSArCiAgIyB0aGVtZQogIHRoZW1lX2J3KCkgKyAKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIGxvZyhwcmljZSkgYW5kIGxvbmdpdHVkZSIpCmBgYApjcmVhdGUgbW9kZWwKCmBgYHtyfQojIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCm1vZGVsXzEgPC0gbG0obG9nX3ByaWNlIH4gbG9uZ2l0dWRlLCBkYXRhID0gcHJpY2VzX3JlZ19kZikKYGBgCgoKYGBge3J9CiMgY2hlY2sgcmVncmVzc2lvbiBkaWFnbm9zdGljcwphdXRvcGxvdChtb2RlbF8xKQpgYGAKCgojIyMgUmVncmVzc2lvbiBEaWFnbm9zdGljcyAKCmdyYXBoIDEgKHBvcHVsYXRpb24pIC0gdGVsbCB1cyBtb3N0IG9ic2VydmF0aW9ucyBhcmUgaW5kZXBlbmRlbmNlLCBibHVlIGxpbmUKZGVjbGluZXMgYXQgdGhlIGVuZCBpbmRpY2F0aW5nIG90aGVyIGNodW5rIG9mIG9ic2VydmF0aW9ucyBhcmUgbm90IGluZGVwZW5kZW50bHkKZGlzdHJpYnV0ZWQuIE5lZWQgYW5vdGhlciB2YXJpYWJsZS4KCmdyYXBoIDIgKGRpc3RyaWJ1dGlvbikgLSBzaG93cyBhIGZhaXJseSBub3JtYWwgZGlzdHJpYnV0aW9uLCBhZ2FpbiwgYSBiZW5kIGF0IHRoZQplbmQgaW5kaWNhdGVzIG1vZGVsIG5lZWRzIG1vcmUgdG8gZ2l2ZSBhIGJldHRlciBkaXN0cmlidXRpb24KCmdyYXBoIDMgKGhvbW9za2VkYXN0aWNpdHkpIC0gVGhlcmUgaXMgaGV0ZXJvc2tlZGFzdGljaXR5IGluIHRoZSBtb2RlbCAKaW5kaWNhdGVkIG15IGN1cnZlIG9uIGJsdWUgbGluZSAKCmdyYXBoIDQgKG91dGxpZXJzKSAtIFRoZXJlIGlzIGEgc21hbGwgbnVtYmVyIG9mIG91dGxpZXJzLCBhbmQgcG9pbnRzIGFyZSBub3QKaGlnaGx5IGxldmVyYWdlZC4gCgoKIyMjIG1vZGVsIGludGVycHJldGF0aW9uCgpgYGB7cn0KIyBvYnRhaW4gcmVzdWx0cyBvZiByZWdyZXNzaW9uIG1vZGVsCnN1bW1hcnkobW9kZWxfMSkKYGBgCgpfX2NvZWZmaWNpZW50OiBfXwoKLSBBbiBpbmNyZWFzZSBpbiBsb25naXR1ZGUgYnkgb25lIHVuaXQgaXMgYXNzb2NpYXRlZCB3aXRoIGEgY2hhbmdlIGluIHByaWNlIGJ5IApfXzk5LjklX18gb24gYXZlcmFnZS4gCgpSLXNxdWFyZWQgLSBsb25naXR1ZGUgZXhwbGFpbnMgMTAuNiUgb2YgdGhlIHZhcmlhbmNlIG9mIGFpcmJuYiBwcmljZS4gCgoKCiMjIE11bHRpdmFyaWF0ZSBSZWdyZXNzaW9uCgpGcm9tIHRoZSBnZ3BhaXJzIHBsb3QgYW5kIHByZXZpb3VzIGFuYWx5c2lzLCBuZWlnaGJvdXJob29kX2dyb3VwIGlzIHN1Z2dlc3RzIApoYXZpbmcgYW4gaW1wYWN0IG9uIGFpcmJuYiBwcmljZXMsIEkgd2lsbCBhZGQgdGhpcyBuZXh0LiAKCgoKIyMgUmVsYXRpb25zaGlwIGJldHdlZW4gUHJpY2UgYW5kIEJvcm91Z2gKCmBgYHtyfQojIHJlbGF0aW9uc2hpcCBsb2cocHJpY2UpIH4gYm9yb3VnaApwcmljZXNfcmVnX2RmICU+JSAKICBnZ3Bsb3QoYWVzKG5laWdoYm91cmhvb2RfZ3JvdXAsIGxvZ19wcmljZSwgZmlsbCA9IG5laWdoYm91cmhvb2RfZ3JvdXApKSArIAogICMgc3BlY2lmeSBib3hwbG90CiAgZ2VvbV9ib3hwbG90KHNob3cubGVnZW5kID0gRkFMU0UpICsKICAjIHRoZW1lCiAgdGhlbWVfYncoKSArIAogICMgdGl0bGUKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIFByaWNlIGFuZCBCb3JvdWdoIikKYGBgCgpgYGB7cn0KIyBsaW5lYXIgbW9kZWwgMgptb2RlbF8yIDwtIGxtKGxvZ19wcmljZSB+IGxvbmdpdHVkZSArIG5laWdoYm91cmhvb2RfZ3JvdXAsIGRhdGEgPSBwcmljZXNfcmVnX2RmKQpgYGAKCgpgYGB7cn0KIyByZWdyZXNzaW9uIGRpYWdub3N0aWNzCmF1dG9wbG90KG1vZGVsXzIpCmBgYAoKCiMjIyByZWdyZXNzaW9uIGRpYWdub3N0aWNzCgpncmFwaCAxIC0gaW5kZXBlbmRlbnQgcG9wdWxhdGlvbiAoYWNoaWV2ZWQpCmdyYXBoIDIgLSBzdGlsbCBhIHNrZXcgaW4gZGlzdHJpYnV0aW9uCmdyYXBoIDMgLSBIb21vc2tkYXN0aWNpdHkgKGFjaGlldmVkKQpncmFwaCA0IC0gTm8gaGlnaGx5IGxldmVyYWdlZCBwb2ludHMsIGJ1dCB0aGVyZSBhcmUgc3RpbGwgKHBvdGVudGlhbGx5KSBhIGZldyAKb3V0bGllcnMKCgojIyMgbW9kZWwgaW50ZXJwcmV0YXRpb24KCmBgYHtyfQojIHJlZ3Jlc3Npb24gcmVzdWx0cwpzdW1tYXJ5KG1vZGVsXzIpCmBgYAoKX19jb2VmZmljaWVudHM6X18gCgotIGFsbCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IGxldmVscyBvZiBzaWduaWZpY2FuY2UgLSBjYW4gcmVqZWN0IHRoZSBudWxsCmh5cG90aGVzaXMgYW5kIHNheSB0aGF0IGNvZWZmaWNpZW50cyBhcmUgc3RhdGlzdGljYWxseSBkaWZmZXJlbnQgZnJvbSB6ZXJvLiAKCi0gIEFuIGluY3JlYXNlIGluIE1hbmhhdHRhbiBieSBvbmUgdW5pdCAoemVybyB0byBvbmUpIGlzIGFzc29jaWF0ZWQgd2l0aCBhIApjaGFuZ2UgaW4gcHJpY2UgYnkgKGVeMC4yNy0xKSAqIDEwMCA9IF9fMzElX18sIGhvbGRpbmcgYWxsIG90aGVyIGZhY3RvcnMgCmNvbnN0YW50IAoKLSBBbiBpbmNyZWFzZSBpbiBTdGF0ZW4gSXNsYW5kIGJ5IG9uZSB1bml0ICh6ZXJvIHRvIG9uZSkgaXMgYXNzb2NpYXRlZCB3aXRoIGEgCmNoYW5nZSBpbiBwcmljZSBieSAoZV4tMC45NCAtIDEpICogMTAwID0gX18tNjAuOSVfXywgaG9sZGluZyBhbGwgb3RoZXIgZmFjdG9ycyAKY29uc3RhbnQKCgojIyMgQW5vdmEgZnVuY3Rpb24KCmBgYHtyfQojIGZ1bmN0aW9uIHRvIGNoZWNrIGlmIGR1bW15IHZhcmlhYmxlcyBhcmUgdXNlZnVsCmFub3ZhKG1vZGVsXzEsIG1vZGVsXzIpCmBgYAoKYW5vdmEgZnVuY3Rpb24gY29uZmlybXMgdGhlIG5laWdoYm91cmhvb2RfZ3JvdXAgZHVtbXkgdmFyaWFibGUgd2FzIGdvb2QgdG8gaW5jbHVkZQppbiB0aGUgbW9kZWwuIAoKCiMjIE1vZGVsIDMgCgpiZWZvcmUgYWRkaW5nIGEgbmV3IHZhcmlhYmxlLCBtdXN0IGNoZWNrIHRoZSByZXNpZHVhbHMgb2YgdGhpcyBtb2RlbCBhZ2FpbnN0IHRoZQpyZW1haW5pbmcgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0CgoKIyMjIGNoZWNrIHJlc2lkdWFscwoKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CgojIG9idGFpbiByZXNpZHVhbHMgb2YgbW9kZWwgMgpyZXNpZHVhbHMgPC0gcHJpY2VzX3JlZ19kZiAlPiUgCiAgIyBhZGQgaW4gcmVzaWR1YWxzIHRvIGRhdGFzZXQKICBhZGRfcmVzaWR1YWxzKG1vZGVsXzIpICU+JSAKICAjIHJlbW92ZSB2YXJpYWJsZXMgYWxyZWFkeSBpbiB0aGUgbW9kZWwKICBkcGx5cjo6c2VsZWN0KC1jKGxvZ19wcmljZSwgbG9uZ2l0dWRlLCBuZWlnaGJvdXJob29kX2dyb3VwKSkKYGBgCgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFfQojIHNlcGVyYXRlIGludG8gbnVtZXJpYyBhbmQgbm9uLW51bWVyaWMgCgojIG51bWVyaWMgZGF0YQpwcmljZV9yZXNpZF9udW1lcmljIDwtIHJlc2lkdWFscyAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykKCiMgbm9uLW51bWVyaWMgZGF0YQpwcmljZV9yZXNpZF9ub25udW1lcmljIDwtIHJlc2lkdWFscyAlPiUKICBzZWxlY3RfaWYoZnVuY3Rpb24oeCkgIWlzLm51bWVyaWMoeCkpCgojIGFkZCByZXNpZHVkYWxzIHRvIG5vbi1udW1lcmljIGRhdGEKcHJpY2VfcmVzaWRfbm9ubnVtZXJpYyRyZXNpZCA8LSByZXNpZHVhbHMkcmVzaWQKCiMgbm93IHJlYWR5IGZvciBjb21wYXJpbmcgbW9kZWwgcmVzaWR1YWxzIHRvIHJlc3Qgb2YgZGF0YQoKIyBudW1lcmljIGdncGFpcnMKZ2dwYWlycyhwcmljZV9yZXNpZF9udW1lcmljKQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIG5vbi1udW1lcmljIGdncGFpcnMKZ2dwYWlycyhwcmljZV9yZXNpZF9ub25udW1lcmljKQpgYGAKCgpGcm9tIG51bWVyaWMgdmFyaWFibGVzLCBhdmFpbGFiaWxpdHlfMzY1IGhhcyB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbiB3aXRoIHByaWNlCmEgcG9zaXRpdmUgY29ycmVsYXRpb24gb2YgMC4xMzEgKHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQpLiAKCkhvd2V2ZXIsIHRoZSBub24tbnVtZXJpYyB2YXJpYWJsZXMgaW5kaWNhdGUgdGhhdCByb29tIHR5cGUgbWF5IHByZXNlbnQgYSBiZXR0ZXIKZXhwbGFuYXRpb24gb2YgcHJpY2UgdmFyaWFuY2UuIAoKCiMjIyBDb21wYXJpc29uIGJldHdlZW4gaW1wYWN0IG9mIFJvb20gVHlwZSBhbmQgQXZhaWxhYmlsaXR5IG9uIFByaWNlCgpgYGB7cn0KIyByZWxhdGlvbnNoaXAgcmVzaWR1YWxzIH4gcm9vbSBhdmFpbGFiaWxpdHkKYXZhaWxfcGxvdCA8LSBwcmljZV9yZXNpZF9udW1lcmljICU+JSAKICBnZ3Bsb3QoYWVzKGF2YWlsYWJpbGl0eV8zNjUsIHJlc2lkKSkgKyAKICAjIHNjYXR0ZXIgcGxvdCAKICBnZW9tX3BvaW50KCkgKwogICMgaW5zZXJ0IGxpbmVhciBtb2RlbCBsaW5lIG9mIGJlc3QgZml0CiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3VyID0gInJlZCIpICsgCiAgIyB0aGVtZQogIHRoZW1lX2J3KCkgKwogICMgdGl0bGVzCiAgbGFicyh4ID0gIlllYXJseSBSb29tIEF2YWlsYWJpbGl0eSIsIHkgPSAiUmVzaWR1YWxzIiwgCiAgICAgICB0aXRsZSA9ICJSZWxhdGlvbnNoaXAgUmVzaWR1YWxzIGFuZCBBdmFpbGFiaWxpdHkiKSArCiAgIyBhZGp1c3QgdGl0bGUgZm9udCBzaXplCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkKCgojIHJlbGF0aW9uc2hpcCByZXNpZHVhbHMgfiByb29tX3R5cGUKcm9vbV90eXBlX3Bsb3QgPC0gcHJpY2VfcmVzaWRfbm9ubnVtZXJpYyAlPiUgCiAgZ2dwbG90KGFlcyhyb29tX3R5cGUsIHJlc2lkLCBmaWxsID0gcm9vbV90eXBlKSkgKyAKICAjIGJveHBsb3QKICBnZW9tX2JveHBsb3Qoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICMgdGhlbWUKICB0aGVtZV9idygpICsgCiAgIyB0aXRsZXMgCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbnNoaXAgUmVzaWR1YWxzIGFuZCBSb29tIFR5cGUiLAogICAgICAgeCA9ICJSb29tIFR5cGUiLCB5ID0gIlJlc2lkdWFscyIpICsKICAjIGFkanVzdCB0aXRsZSBmb250IHNpemUKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKQoKCiMgcGxvdCBib3RoIHZpc3VhbHMgdG9nZXRoZXIKY293cGxvdDo6cGxvdF9ncmlkKGF2YWlsX3Bsb3QsIHJvb21fdHlwZV9wbG90LCBucm93ID0gMSkgCmBgYAoKVGhlIGNvbXBhcmlzb24gc3VnZ2VzdHMgcm9vbSB0eXBlIHNob3VsZCBvZmZlciBtb3JlIGV4cGxhbmF0aW9uLiAKCgpgYGB7cn0KIyBsaW5lYXIgbW9kZWwgMwptb2RlbF8zIDwtIGxtKGxvZ19wcmljZSB+IGxvbmdpdHVkZSArIG5laWdoYm91cmhvb2RfZ3JvdXAgKyByb29tX3R5cGUsIAogICAgICAgICAgICAgIGRhdGEgPSBwcmljZXNfcmVnX2RmKQpgYGAKCgpgYGB7cn0KIyBjaGVjayBtb2RlbCBkaWFnbm9zdGljcwphdXRvcGxvdChtb2RlbF8zKQpgYGAKCgpncmFwaCAxIC0gcmVzaWR1YWxzIGFyZSBpbmRlcGVuZGVudApncmFwaCAyIC0gdGhlIHJlc2lkdWFscyBzZWVtIHRvIGJlIGluY3JlYXNpbmdseSBub3QgZGlzdHJpYnV0ZWQgYXJvdW5kIHplcm8Kd2l0aCBtb3JlIHNrZXcgYXQgdGhlIGJlZ2lubmluZyBhbmQgZW5kCmdyYXBoIDMgLSBjb25kaXRpb25hbCB2YXJpYW5jZSBvZiByZXNpZHVhbHMgaXMgY29uc3RhbnQgKGhvbW9za2VkYXN0aWNpdHkpCgoKYGBge3J9CiMgbW9kZWwgcmVzdWx0cwpzdW1tYXJ5KG1vZGVsXzMpCmBgYAoKCkFzIGFudGljaXBhdGVkLCByb29tX3R5cGUgZ3JlYXRseSBlbmhhbmNlZCB0aGUgZXhwbGFuYXRpb24gb2YgdGhlIG1vZGVsLiBUaGUgClItc3F1YXJlZCBzdWdnZXN0cyB0aGUgbW9kZWwgZXhwbGFpbnMgNDkuMyUgb2YgdGhlIHZhcmlhbmNlIGluIHByaWNlLiAKClRoZSB2YXJpYWJsZSBjb2VmZmljaWVudHMgYXJlIGFsbCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHRoZXJlZm9yZSBjYW4gYmUKaW50ZXJwcmV0ZWQuIAoKCiMjIE1vZGVsIDQKCiMjIyBDaGVjayByZXNpZHVhbHMKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIGNvbXBhcmUgbW9kZWwgcmVzaWR1YWxzIHRvIHJlbWFpbmluZyBkYXRhCnJlc2lkdWFscyA8LSBwcmljZXNfcmVnX2RmICU+JSAKICAjIGFkZCByZXNpZHVhbHMKICBhZGRfcmVzaWR1YWxzKG1vZGVsXzMpICU+JSAKICAjIHJlbW92ZSB2YXJpYWJsZXMgaW4gbW9kZWwKICBkcGx5cjo6c2VsZWN0KC1jKGxvZ19wcmljZSwgbG9uZ2l0dWRlLCBuZWlnaGJvdXJob29kX2dyb3VwLCByb29tX3R5cGUpKQpgYGAKCgpgYGB7ciBtZXNzYWdlID0gRkFMU0V9CgojIHNlcGVyYXRlIGludG8gbnVtZXJpYyBhbmQgbm9uLW51bWVyaWMgCgojIG51bWVyaWMgZGF0YQpwcmljZV9yZXNpZF9udW1lcmljIDwtIHJlc2lkdWFscyAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykKCiMgbm9uLW51bWVyaWMgZGF0YQpwcmljZV9yZXNpZF9ub25udW1lcmljIDwtIHJlc2lkdWFscyAlPiUKICBzZWxlY3RfaWYoZnVuY3Rpb24oeCkgIWlzLm51bWVyaWMoeCkpCgoKIyBhZGQgcmVzaWR1YWxzIHRvIG5vbi1udW1lcmljIGRhdGEKcHJpY2VfcmVzaWRfbm9ubnVtZXJpYyRyZXNpZCA8LSByZXNpZHVhbHMkcmVzaWQKCiMgbnVtZXJpYyBnZ3BhaXJzCmdncGFpcnMocHJpY2VfcmVzaWRfbnVtZXJpYykKYGBgCgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFfQojIG5vbi1udW1lcmljIGdncGFpcnMKZ2dwYWlycyhwcmljZV9yZXNpZF9ub25udW1lcmljKQpgYGAKCgpBcyBiZWZvcmUsIGF2YWlsYWJpbGl0eSBpcyBkaXNwbGF5aW5nIGJ5IGZhciB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uLCBhbmQgdGhlcmUKZG9lcyBub3Qgc2VlbSB0byBiZSBhbnkgc3RhbmQgb3V0IG5vbi1udW1lcmljIHZhcmlhYmxlcy4gVGhlcmVmb3JlLCByb29tIAphdmFpbGFiaWxpdHkgd2lsbCBiZSB1c2VkIGluIHRoZSBuZXh0IG1vZGVsLiAKCgpgYGB7cn0KIyBsaW5lYXIgbW9kZWwgNAptb2RlbF80IDwtIGxtKGxvZ19wcmljZSB+IGxvbmdpdHVkZSArIG5laWdoYm91cmhvb2RfZ3JvdXAgKyByb29tX3R5cGUgKyAKICAgICAgICAgICAgICAgIGF2YWlsYWJpbGl0eV8zNjUsIGRhdGEgPSBwcmljZXNfcmVnX2RmKQpgYGAKCgoKYGBge3J9CiMgY2hlY2sgbW9kZWwgZGlhZ25vc3RpY3MKYXV0b3Bsb3QobW9kZWxfNCkKYGBgCgpncmFwaCAxIC0gbW9kZWwgcG9wdWxhdGlvbiBpcyBpbmRlcGVuZGVudGx5IGRpc3RyaWJ1dGVkCmdyYXBoIDIgLSBzdGlsbCBza2V3IGF0IGJvdGggZW5kcyBvZiBncmFwaCwgaW5kaWNhdGluZyBncmFwaCBpcyBvbmx5IHNvbWV3aGF0Cm5vcm1hbGx5IGRpc3RyaWJ1dGVkCmdyYXBoIDMgLSBjb25kaXRpb25hbCB2YXJpYW5jZSBvZiByZXNpZHVhbHMgaXMgY29uc3RhbnQgKGhvbW9za2VkYXN0aWMpCgoKCmBgYHtyfQojIG1vZGVsIHJlc3VsdHMKc3VtbWFyeShtb2RlbF80KQpgYGAKCgpSLXNxdWFyZWQgaGFzIG9ubHkgaW5jcmVhc2VkIG1hcmdpbmFsbHkgdG8gMC41MSAtIG1vZGVsIGV4cGxhaW5zIDUxJSBvZiB0aGUgCnZhcmlhbmNlIGluIHByaWNlCgpBbGwgdmFyaWFibGVzIGluIHRoZSBtb2RlbCBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudAoKCgojIyBBZGRpbmcgYW4gaW50ZXJhY3Rpb24gdGVybQoKcG90ZW50aWFsIHRlcm1zOgoKLSBsb25naXR1ZGU6bmVpZ2hib3VyaG9vZF9ncm91cAotIGxvbmdpdHVkZTpyb29tX3R5cGUKLSBsb25naXR1ZGU6YXZhaWxhYmlsaXR5XzM2NQotIG5laWdoYm91cmhvb2RfZ3JvdXA6cm9vbV90eXBlCi0gbmVpZ2hib3VyaG9vZF9ncm91cDphdmFpbGFiaWxpdHlfMzY1Ci0gcm9vbV90eXBlOmF2YWlsYWJpbGl0eV8zNjUKCgpUaHJvdWdoIHByb2Nlc3Mgb2YgZWxpbWluYXRpb24sIGxvbmdpdHVkZTpuZWlnaGJvdXJob29kX2dyb3VwIAoKYGBge3J9CiMgbGluZWFyIG1vZGVsIDUgLSB3aXRoIGludGVyYWN0aW9uIHRlcm0KbW9kZWxfNSA8LSBsbShsb2dfcHJpY2UgfiBsb25naXR1ZGUgKyBuZWlnaGJvdXJob29kX2dyb3VwICsgcm9vbV90eXBlICsgCiAgICAgICAgICAgICAgICBhdmFpbGFiaWxpdHlfMzY1ICsgbG9uZ2l0dWRlOm5laWdoYm91cmhvb2RfZ3JvdXAsCiAgICAgICAgICAgICAgZGF0YSA9IHByaWNlc19yZWdfZGYpCmBgYAoKCiMjIyBQbG90dGluZyB0aGUgaW50ZXJhY3Rpb24gCgpgYGB7cn0KIyBhZGQgbW9kZWwgcmVzaWR1YWxzIHRvIGRhdGEKcHJpY2VfcmVzaWQgPC0gcHJpY2VzX3JlZ19kZiAlPiUgCiAgIyBhZGQgbW9kZWwgcmVzaWR1YWxzCiAgYWRkX3Jlc2lkdWFscyhtb2RlbF81KSAlPiUgCiAgIyByZW1vdmUgbG9nX3ByaWNlCiAgZHBseXI6OnNlbGVjdCgtbG9nX3ByaWNlKQoKCiMgY2hlY2sgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gbG9uZ2l0dWRlIGFuZCBuZWlnaGJvdXJob29kX2dyb3VwCmNvcGxvdChyZXNpZCB+IGxvbmdpdHVkZSB8IG5laWdoYm91cmhvb2RfZ3JvdXAsCiAgICAgICAjIGdpdmUgYW4gYWN0aW9uIHRvIGJlIGNhcnJpZWQgb3V0IGluIGVhY2ggcGFuZWwKICAgICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKXsKICAgICAgICAgIyBwbG90IGNvb3JkaW5hdGVzIG9mIHggYW5kIHkKICAgICAgICAgcG9pbnRzKHgsIHkpCiAgICAgICAgICMgaW5zZXJ0IGxpbmVhciBtb2RlbCBsaW5lCiAgICAgICAgIGFibGluZShsbSh5IH4geCksIGNvbCA9ICJibHVlIikKICAgICAgIH0sCiAgICAgICBkYXRhID0gcHJpY2VfcmVzaWQsIHJvd3MgPSAxKQpgYGAKCgojIyMgTW9kZWwgRGlhZ25vc3RpY3MKCmBgYHtyfQojIHJlZ3Jlc3Npb24gZGlhZ25vc3RpY3MKYXV0b3Bsb3QobW9kZWxfNSkKYGBgCgpncmFwaCAxIC0gcG9wdWxhdGlvbiBpcyBpbmRlcGVuZGVudApncmFwaCAyIC0gbW9kZWwgc3RpbGwgbm90IGNvbXBsZXRlbHkgZXZlbmx5IGRpc3RyaWJ1dGVkIGFyb3VuZCAwCmdyYXBoIDMgLSBjb25kaXRpb25hbCB2YXJpYW5jZSBvZiByZXNpZHVhbHMgaXMgY29uc3RhbnQgKGhvbW9za2VkYXN0aWNpdHkpCmdyYXBoIDQgLSB0aGVyZSBhcmUgc3RpbGwgc29tZSBvdXRsaWVycywgYnV0IG5vIGhpZ2hseSBsZXZlcmFnZWQgcG9pbnRzCgoKIyMjIE1vZGVsIFN1bW1hcnkKCmBgYHtyfQojIG1vZGVsIHJlc3VsdHMKc3VtbWFyeShtb2RlbF81KQpgYGAKX19Nb2RlbCBTdW1tYXJ5Ol9fCgotIEFsbCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQKCi0gUi1zcXVhcmVkOiBtb2RlbCBleHBsYWlucyA1Mi41JSBvZiB2YXJpYW5jZSBpbiBwcmljZQogIC0gbW9kZWwgc3VmZmljZXMgYXMgYW4gb2sgZXhwbGFuYXRpb24KICAKLSBSb29tX3R5cGUgaGFkIHRoZSBncmVhdGVzdCBpbmZsdWVuY2Ugb24gdGhlIG1vZGVsCiAgLSBpbnRlcnByZXQgdGhlIGNvZWZmaWNpZW50IGZvciByb29tX3R5cGUtIGVudGlyZS9hcHQ6CiAgICAtIEFuIGluY3JlYXNlIGluIGVudGlyZS9hcHQgYnkgb25lIHVuaXQgKHplcm8gdG8gb25lKSBpcyBhc3NvY2lhdGVkIHdpdGggYSAKY2hhbmdlIGluIHByaWNlIGJ5IChlXjAuNzMtMSkgKiAxMDAgPSBfXzEwNy41JV9fLCBob2xkaW5nIGFsbCBvdGhlciBmYWN0b3JzIApjb25zdGFudAoKCiMjIyBWYXJpYWJsZSByZWxhdGl2ZSBpbXBvcnRhbmNlIAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KCiMgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHZhcmlhYmxlIGltcG9ydGFuY2UKY2FsYy5yZWxpbXAobW9kZWxfNSwgdHlwZSA9ICJsbWciLCByZWxhID0gVFJVRSkKYGBgCgpfX1ZhcmlhYmxlcyBvZiByZWxhdGl2ZSBpbXBvcnRhbmNlOl9fIAoKUm9vbSB0eXBlLCBwZXJoYXBzIHVuc3VycHJpc2luZ2x5LCBpcyB0aGUgbW9zdCByZWxldmFudCB2YXJpYWJsZSBmb3IgZXhwbGFpbmluZwp2YXJpYXRpb25zIGluIHByaWNlLiAKVGhlIGxlYXN0IGV4cGxhbmF0b3J5IGlzIGF2YWlsYWJpbGl0eV8zNjUKCgojIENvbmNsdXNpb24gYW5kIFJlY29tbWVuZGF0aW9ucwoKX19Gb3IgQ29uc3VtZXJzOl9fIAoKLSBUaGUgcm9vbSB0eXBlIGFkdmVydGlzZWQgaXMgdmVyeSBpbXBvcnRhbnQgdG8gZGV0ZXJtaW5pbmcgdGhlIHByaWNlIHlvdSBwYXkKLSBsb2NhdGlvbiBhbmFseXNpcyBzdWdnZXN0cyBjaXR5IGNlbnRyZSBhcmVhcyBkZW1hbmQgbXVjaCBoaWdoZXIgcHJpY2VzIHRoYW4gCm91dHNraXJ0IGFyZWFzIHN1Y2ggYXMgU3RhdGVuIElzbGFuZAoKX19Gb3IgaG9zdHM6X18gCi0gSG93IHlvdSBkZXNjcmliZSB5b3VyIHByb3BlcnR5IGRvZXNuJ3QgdHJhbnNsYXRlIHRvIGJlaW5nIGFibGUgdG8gY2hhcmdlCmhpZ2hlciBwcmljZXMKLSB3aWxsIGJlIGFibGUgdG8gY2hhcmdlIGEgbXVjaCBoaWdoZXIgcHJpY2UgZm9yIHByb3BlcnR5IGxpc3RlZCBhcyB3aG9sZSBhcGFydG1lbnQKLSBwcm9wZXJ0eSBhdmFpbGFiaWxpdHkgZG9lcyBub3QgaGF2ZSBtdWNoIGltcGFjdCBvbiBwcmljZQoKCiMgRnV0dXJlIGFuYWx5c2lzCgpNb3JlIGRhdGEgb246CgoteWVhcmx5IGRhdGEKICAtIHRpbWUtc2VyaWVzIGFuYWx5c2lzCiAgICAtIGlkZW50aWZ5IGN5Y2xpY2FsIHRyZW5kcw==